diff --git a/docs/changelog/126612.yaml b/docs/changelog/126612.yaml new file mode 100644 index 0000000000000..e8fd1825bfc2d --- /dev/null +++ b/docs/changelog/126612.yaml @@ -0,0 +1,5 @@ +pr: 126612 +summary: Add Support for Providing a custom `ServiceAccountTokenStore` through `SecurityExtensions` +area: Authentication +type: enhancement +issues: [] diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityExtension.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityExtension.java index 609822a4bd20b..9226a4148900d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityExtension.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityExtension.java @@ -16,6 +16,8 @@ import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xpack.core.security.authc.AuthenticationFailureHandler; import org.elasticsearch.xpack.core.security.authc.Realm; +import org.elasticsearch.xpack.core.security.authc.service.NodeLocalServiceAccountTokenStore; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountTokenStore; import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper; import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; @@ -114,6 +116,18 @@ default List, ActionListener>> getRo return Collections.emptyList(); } + /** + * Returns a {@link NodeLocalServiceAccountTokenStore} used to authenticate service account tokens. + * If {@code null} is returned, the default service account token stores will be used. + * + * Providing a custom {@link NodeLocalServiceAccountTokenStore} here overrides the default implementation. + * + * @param components Access to components that can be used to authenticate service account tokens + */ + default ServiceAccountTokenStore getServiceAccountTokenStore(SecurityComponents components) { + return null; + } + /** * Returns a authorization engine for authorizing requests, or null to use the default authorization mechanism. * diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/service/NodeLocalServiceAccountTokenStore.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/service/NodeLocalServiceAccountTokenStore.java new file mode 100644 index 0000000000000..688d5a98f3972 --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/service/NodeLocalServiceAccountTokenStore.java @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.core.security.authc.service; + +import org.elasticsearch.xpack.core.security.action.service.TokenInfo; + +import java.util.List; + +public interface NodeLocalServiceAccountTokenStore extends ServiceAccountTokenStore { + default List findNodeLocalTokensFor(ServiceAccount.ServiceAccountId accountId) { + throw new IllegalStateException("Find node local tokens not supported by [" + this.getClass() + "]"); + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ServiceAccount.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/service/ServiceAccount.java similarity index 97% rename from x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ServiceAccount.java rename to x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/service/ServiceAccount.java index 3ac5fcf0d0b80..effc5c66baedb 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ServiceAccount.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/service/ServiceAccount.java @@ -5,7 +5,7 @@ * 2.0. */ -package org.elasticsearch.xpack.security.authc.service; +package org.elasticsearch.xpack.core.security.authc.service; import org.apache.logging.log4j.util.Strings; import org.elasticsearch.common.io.stream.StreamInput; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountToken.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/service/ServiceAccountToken.java similarity index 97% rename from x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountToken.java rename to x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/service/ServiceAccountToken.java index ce509a0122419..03f8fe3554383 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountToken.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/service/ServiceAccountToken.java @@ -5,7 +5,7 @@ * 2.0. */ -package org.elasticsearch.xpack.security.authc.service; +package org.elasticsearch.xpack.core.security.authc.service; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -14,9 +14,9 @@ import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.core.CharArrays; import org.elasticsearch.xpack.core.security.authc.AuthenticationToken; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount.ServiceAccountId; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import org.elasticsearch.xpack.core.security.support.Validation; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -51,7 +51,6 @@ public class ServiceAccountToken implements AuthenticationToken, Closeable { private final ServiceAccountTokenId tokenId; private final SecureString secret; - // pkg private for testing ServiceAccountToken(ServiceAccountId accountId, String tokenName, SecureString secret) { tokenId = new ServiceAccountTokenId(accountId, tokenName); this.secret = Objects.requireNonNull(secret, "service account token secret cannot be null"); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountTokenStore.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/service/ServiceAccountTokenStore.java similarity index 62% rename from x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountTokenStore.java rename to x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/service/ServiceAccountTokenStore.java index 37eec6a092e3f..3226ee1381d8c 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountTokenStore.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/service/ServiceAccountTokenStore.java @@ -5,7 +5,7 @@ * 2.0. */ -package org.elasticsearch.xpack.security.authc.service; +package org.elasticsearch.xpack.core.security.authc.service; import org.elasticsearch.action.ActionListener; import org.elasticsearch.xpack.core.security.action.service.TokenInfo.TokenSource; @@ -24,11 +24,23 @@ class StoreAuthenticationResult { private final boolean success; private final TokenSource tokenSource; - public StoreAuthenticationResult(boolean success, TokenSource tokenSource) { + private StoreAuthenticationResult(TokenSource tokenSource, boolean success) { this.success = success; this.tokenSource = tokenSource; } + public static StoreAuthenticationResult successful(TokenSource tokenSource) { + return new StoreAuthenticationResult(tokenSource, true); + } + + public static StoreAuthenticationResult failed(TokenSource tokenSource) { + return new StoreAuthenticationResult(tokenSource, false); + } + + public static StoreAuthenticationResult fromBooleanResult(TokenSource tokenSource, boolean result) { + return result ? successful(tokenSource) : failed(tokenSource); + } + public boolean isSuccess() { return success; } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountTokenTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/service/ServiceAccountTokenTests.java similarity index 96% rename from x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountTokenTests.java rename to x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/service/ServiceAccountTokenTests.java index 6c8c625c0ceea..dbece9ca5b4a4 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountTokenTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/service/ServiceAccountTokenTests.java @@ -5,13 +5,13 @@ * 2.0. */ -package org.elasticsearch.xpack.security.authc.service; +package org.elasticsearch.xpack.core.security.authc.service; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount.ServiceAccountId; import org.elasticsearch.xpack.core.security.support.Validation; import org.elasticsearch.xpack.core.security.support.ValidationTests; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; import java.io.IOException; diff --git a/x-pack/plugin/security/src/main/java/module-info.java b/x-pack/plugin/security/src/main/java/module-info.java index 4820fd4ad6096..7af53479b0844 100644 --- a/x-pack/plugin/security/src/main/java/module-info.java +++ b/x-pack/plugin/security/src/main/java/module-info.java @@ -74,6 +74,7 @@ exports org.elasticsearch.xpack.security.rest.action.apikey to org.elasticsearch.internal.security; exports org.elasticsearch.xpack.security.support to org.elasticsearch.internal.security; exports org.elasticsearch.xpack.security.authz.store to org.elasticsearch.internal.security; + exports org.elasticsearch.xpack.security.authc.service; provides org.elasticsearch.index.SlowLogFieldProvider with org.elasticsearch.xpack.security.slowlog.SecuritySlowLogFieldProvider; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index 004ea9a23ee70..60b034294bcfc 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -208,6 +208,8 @@ import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.Subject; +import org.elasticsearch.xpack.core.security.authc.service.NodeLocalServiceAccountTokenStore; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountTokenStore; import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine; @@ -310,6 +312,7 @@ import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm; import org.elasticsearch.xpack.security.authc.jwt.JwtRealm; import org.elasticsearch.xpack.security.authc.service.CachingServiceAccountTokenStore; +import org.elasticsearch.xpack.security.authc.service.CompositeServiceAccountTokenStore; import org.elasticsearch.xpack.security.authc.service.FileServiceAccountTokenStore; import org.elasticsearch.xpack.security.authc.service.IndexServiceAccountTokenStore; import org.elasticsearch.xpack.security.authc.service.ServiceAccountService; @@ -915,12 +918,34 @@ Collection createComponents( this.realms.set(realms); systemIndices.getMainIndexManager().addStateListener(nativeRoleMappingStore::onSecurityIndexStateChange); - final CacheInvalidatorRegistry cacheInvalidatorRegistry = new CacheInvalidatorRegistry(); - cacheInvalidatorRegistry.registerAlias("service", Set.of("file_service_account_token", "index_service_account_token")); components.add(cacheInvalidatorRegistry); - systemIndices.getMainIndexManager().addStateListener(cacheInvalidatorRegistry::onSecurityIndexStateChange); + ServiceAccountService serviceAccountService = createServiceAccountService( + components, + cacheInvalidatorRegistry, + extensionComponents, + () -> new IndexServiceAccountTokenStore( + settings, + threadPool, + getClock(), + client, + systemIndices.getMainIndexManager(), + clusterService, + cacheInvalidatorRegistry + ), + () -> new FileServiceAccountTokenStore( + environment, + resourceWatcherService, + threadPool, + clusterService, + cacheInvalidatorRegistry + ) + ); + + components.add(serviceAccountService); + + systemIndices.getMainIndexManager().addStateListener(cacheInvalidatorRegistry::onSecurityIndexStateChange); final NativePrivilegeStore privilegeStore = new NativePrivilegeStore( settings, client, @@ -1004,33 +1029,6 @@ Collection createComponents( ); components.add(apiKeyService); - final IndexServiceAccountTokenStore indexServiceAccountTokenStore = new IndexServiceAccountTokenStore( - settings, - threadPool, - getClock(), - client, - systemIndices.getMainIndexManager(), - clusterService, - cacheInvalidatorRegistry - ); - components.add(indexServiceAccountTokenStore); - - final FileServiceAccountTokenStore fileServiceAccountTokenStore = new FileServiceAccountTokenStore( - environment, - resourceWatcherService, - threadPool, - clusterService, - cacheInvalidatorRegistry - ); - components.add(fileServiceAccountTokenStore); - - final ServiceAccountService serviceAccountService = new ServiceAccountService( - client, - fileServiceAccountTokenStore, - indexServiceAccountTokenStore - ); - components.add(serviceAccountService); - final RoleProviders roleProviders = new RoleProviders( reservedRolesStore, fileRolesStore.get(), @@ -1250,6 +1248,74 @@ Collection createComponents( return components; } + private ServiceAccountService createServiceAccountService( + List components, + CacheInvalidatorRegistry cacheInvalidatorRegistry, + SecurityExtension.SecurityComponents extensionComponents, + Supplier indexServiceAccountTokenStoreSupplier, + Supplier fileServiceAccountTokenStoreSupplier + ) { + Map accountTokenStoreByExtension = new HashMap<>(); + + for (var extension : securityExtensions) { + var serviceAccountTokenStore = extension.getServiceAccountTokenStore(extensionComponents); + if (serviceAccountTokenStore != null) { + if (isInternalExtension(extension) == false) { + throw new IllegalStateException( + "The [" + + extension.getClass().getName() + + "] extension tried to install a custom ServiceAccountTokenStore. This functionality is not available to " + + "external extensions." + ); + } + accountTokenStoreByExtension.put(extension.extensionName(), serviceAccountTokenStore); + } + } + + if (accountTokenStoreByExtension.size() > 1) { + throw new IllegalStateException( + "More than one extension provided a ServiceAccountTokenStore override: " + accountTokenStoreByExtension.keySet() + ); + } + + if (accountTokenStoreByExtension.isEmpty()) { + var fileServiceAccountTokenStore = fileServiceAccountTokenStoreSupplier.get(); + var indexServiceAccountTokenStore = indexServiceAccountTokenStoreSupplier.get(); + + components.add(new PluginComponentBinding<>(NodeLocalServiceAccountTokenStore.class, fileServiceAccountTokenStore)); + components.add(fileServiceAccountTokenStore); + components.add(indexServiceAccountTokenStore); + cacheInvalidatorRegistry.registerAlias("service", Set.of("file_service_account_token", "index_service_account_token")); + + return new ServiceAccountService( + client.get(), + new CompositeServiceAccountTokenStore( + List.of(fileServiceAccountTokenStore, indexServiceAccountTokenStore), + client.get().threadPool().getThreadContext() + ), + indexServiceAccountTokenStore + ); + } + // Completely handover service account token management to the extension if provided, + // this will disable the index managed + // service account tokens managed through the service account token API + var extensionStore = accountTokenStoreByExtension.values().stream().findFirst(); + components.add(new PluginComponentBinding<>(NodeLocalServiceAccountTokenStore.class, (token, listener) -> { + throw new IllegalStateException("Node local config not supported by [" + extensionStore.get().getClass() + "]"); + })); + components.add(extensionStore); + logger.debug("Service account authentication handled by extension, disabling file and index token stores"); + return new ServiceAccountService(client.get(), extensionStore.get()); + } + + private static boolean isInternalExtension(SecurityExtension extension) { + final String canonicalName = extension.getClass().getCanonicalName(); + if (canonicalName == null) { + return false; + } + return canonicalName.startsWith("org.elasticsearch.xpack.") || canonicalName.startsWith("co.elastic.elasticsearch."); + } + @FixForMultiProject // TODO : The migration task needs to be project aware private void applyPendingSecurityMigrations(ProjectId projectId, SecurityIndexManager.IndexState newState) { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/service/TransportGetServiceAccountAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/service/TransportGetServiceAccountAction.java index 2e882546cb5a6..be0a93bbda207 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/service/TransportGetServiceAccountAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/service/TransportGetServiceAccountAction.java @@ -19,7 +19,7 @@ import org.elasticsearch.xpack.core.security.action.service.GetServiceAccountRequest; import org.elasticsearch.xpack.core.security.action.service.GetServiceAccountResponse; import org.elasticsearch.xpack.core.security.action.service.ServiceAccountInfo; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount; import org.elasticsearch.xpack.security.authc.service.ServiceAccountService; import java.util.function.Predicate; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/service/TransportGetServiceAccountNodesCredentialsAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/service/TransportGetServiceAccountNodesCredentialsAction.java index 82ec407189595..55352086a58a6 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/service/TransportGetServiceAccountNodesCredentialsAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/service/TransportGetServiceAccountNodesCredentialsAction.java @@ -21,8 +21,8 @@ import org.elasticsearch.xpack.core.security.action.service.GetServiceAccountCredentialsNodesResponse; import org.elasticsearch.xpack.core.security.action.service.GetServiceAccountNodesCredentialsAction; import org.elasticsearch.xpack.core.security.action.service.TokenInfo; -import org.elasticsearch.xpack.security.authc.service.FileServiceAccountTokenStore; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; +import org.elasticsearch.xpack.core.security.authc.service.NodeLocalServiceAccountTokenStore; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount.ServiceAccountId; import java.io.IOException; import java.util.List; @@ -38,7 +38,7 @@ public class TransportGetServiceAccountNodesCredentialsAction extends TransportN GetServiceAccountCredentialsNodesResponse.Node, Void> { - private final FileServiceAccountTokenStore fileServiceAccountTokenStore; + private final NodeLocalServiceAccountTokenStore readOnlyServiceAccountTokenStore; @Inject public TransportGetServiceAccountNodesCredentialsAction( @@ -46,7 +46,7 @@ public TransportGetServiceAccountNodesCredentialsAction( ClusterService clusterService, TransportService transportService, ActionFilters actionFilters, - FileServiceAccountTokenStore fileServiceAccountTokenStore + NodeLocalServiceAccountTokenStore readOnlyServiceAccountTokenStore ) { super( GetServiceAccountNodesCredentialsAction.NAME, @@ -56,7 +56,7 @@ public TransportGetServiceAccountNodesCredentialsAction( GetServiceAccountCredentialsNodesRequest.Node::new, threadPool.executor(ThreadPool.Names.GENERIC) ); - this.fileServiceAccountTokenStore = fileServiceAccountTokenStore; + this.readOnlyServiceAccountTokenStore = readOnlyServiceAccountTokenStore; } @Override @@ -84,7 +84,7 @@ protected GetServiceAccountCredentialsNodesResponse.Node nodeOperation( Task task ) { final ServiceAccountId accountId = new ServiceAccountId(request.getNamespace(), request.getServiceName()); - final List tokenInfos = fileServiceAccountTokenStore.findTokensFor(accountId); + final List tokenInfos = readOnlyServiceAccountTokenStore.findNodeLocalTokensFor(accountId); return new GetServiceAccountCredentialsNodesResponse.Node( clusterService.localNode(), tokenInfos.stream().map(TokenInfo::getName).toArray(String[]::new) 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 8f9d754b0c0a5..4799caa360a0e 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 @@ -97,6 +97,7 @@ 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.service.ServiceAccountToken; import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AuthorizationInfo; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges; @@ -110,7 +111,6 @@ import org.elasticsearch.xpack.security.audit.AuditTrail; import org.elasticsearch.xpack.security.audit.AuditUtil; import org.elasticsearch.xpack.security.authc.ApiKeyService; -import org.elasticsearch.xpack.security.authc.service.ServiceAccountToken; import org.elasticsearch.xpack.security.rest.RemoteHostHeader; import org.elasticsearch.xpack.security.transport.filter.IPFilter; import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ServiceAccountAuthenticator.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ServiceAccountAuthenticator.java index eb907b3bb52cd..2fbc792d1c62d 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ServiceAccountAuthenticator.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ServiceAccountAuthenticator.java @@ -15,8 +15,8 @@ import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.AuthenticationResult; import org.elasticsearch.xpack.core.security.authc.AuthenticationToken; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; import org.elasticsearch.xpack.security.authc.service.ServiceAccountService; -import org.elasticsearch.xpack.security.authc.service.ServiceAccountToken; import org.elasticsearch.xpack.security.metric.InstrumentedSecurityActionListener; import org.elasticsearch.xpack.security.metric.SecurityMetricType; import org.elasticsearch.xpack.security.metric.SecurityMetrics; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/CachingServiceAccountTokenStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/CachingServiceAccountTokenStore.java index fff47c2c22e2e..23ffe98fcdf95 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/CachingServiceAccountTokenStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/CachingServiceAccountTokenStore.java @@ -19,6 +19,8 @@ import org.elasticsearch.core.TimeValue; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xpack.core.security.action.service.TokenInfo.TokenSource; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountTokenStore; import org.elasticsearch.xpack.core.security.authc.support.Hasher; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; @@ -97,10 +99,10 @@ private void authenticateWithCache(ServiceAccountToken token, ActionListener { if (result.success) { - l.onResponse(new StoreAuthenticationResult(result.verify(token), getTokenSource())); + l.onResponse(StoreAuthenticationResult.fromBooleanResult(getTokenSource(), result.verify(token))); } else if (result.verify(token)) { // same wrong token - l.onResponse(new StoreAuthenticationResult(false, getTokenSource())); + l.onResponse(StoreAuthenticationResult.failed(getTokenSource())); } else { cache.invalidate(token.getQualifiedName(), listenableCacheEntry); authenticateWithCache(token, l); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/CompositeServiceAccountTokenStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/CompositeServiceAccountTokenStore.java index e5227e3b9f593..48a8a89cda300 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/CompositeServiceAccountTokenStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/CompositeServiceAccountTokenStore.java @@ -12,6 +12,8 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.xpack.core.common.IteratingActionListener; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountTokenStore; import java.util.List; import java.util.function.Function; @@ -38,7 +40,7 @@ public void authenticate(ServiceAccountToken token, ActionListener false == storeAuthenticationResult.isSuccess() + storeAuthenticationResult -> storeAuthenticationResult.isSuccess() == false ); try { authenticatingListener.run(); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccounts.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccounts.java index dd671ebef824e..7caab9d5a6e87 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccounts.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccounts.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.security.authc.service; import org.elasticsearch.common.Strings; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; import org.elasticsearch.xpack.core.security.user.User; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileServiceAccountTokenStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileServiceAccountTokenStore.java index dff79c56b32dc..f7921d1ae3526 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileServiceAccountTokenStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileServiceAccountTokenStore.java @@ -22,10 +22,12 @@ import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.security.action.service.TokenInfo; import org.elasticsearch.xpack.core.security.action.service.TokenInfo.TokenSource; +import org.elasticsearch.xpack.core.security.authc.service.NodeLocalServiceAccountTokenStore; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount.ServiceAccountId; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; import org.elasticsearch.xpack.core.security.authc.support.Hasher; import org.elasticsearch.xpack.core.security.support.NoOpLogger; import org.elasticsearch.xpack.security.PrivilegedFileWatcher; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; import org.elasticsearch.xpack.security.support.FileLineParser; import org.elasticsearch.xpack.security.support.FileReloadListener; @@ -41,7 +43,7 @@ import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; -public class FileServiceAccountTokenStore extends CachingServiceAccountTokenStore { +public class FileServiceAccountTokenStore extends CachingServiceAccountTokenStore implements NodeLocalServiceAccountTokenStore { private static final Logger logger = LogManager.getLogger(FileServiceAccountTokenStore.class); @@ -50,6 +52,7 @@ public class FileServiceAccountTokenStore extends CachingServiceAccountTokenStor private final CopyOnWriteArrayList refreshListeners; private volatile Map tokenHashes; + @SuppressWarnings("this-escape") public FileServiceAccountTokenStore( Environment env, ResourceWatcherService resourceWatcherService, @@ -82,8 +85,8 @@ public void doAuthenticate(ServiceAccountToken token, ActionListener new StoreAuthenticationResult(Hasher.verifyHash(token.getSecret(), hash), getTokenSource())) - .orElse(new StoreAuthenticationResult(false, getTokenSource())) + .map(hash -> StoreAuthenticationResult.fromBooleanResult(getTokenSource(), Hasher.verifyHash(token.getSecret(), hash))) + .orElse(StoreAuthenticationResult.failed(getTokenSource())) ); } @@ -92,7 +95,8 @@ public TokenSource getTokenSource() { return TokenSource.FILE; } - public List findTokensFor(ServiceAccountId accountId) { + @Override + public List findNodeLocalTokensFor(ServiceAccountId accountId) { final String principal = accountId.asPrincipal(); return tokenHashes.keySet() .stream() diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java index 14ca1663e16a5..9549e91d8a49d 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java @@ -21,10 +21,11 @@ import org.elasticsearch.core.Predicates; import org.elasticsearch.env.Environment; import org.elasticsearch.xpack.core.XPackSettings; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount.ServiceAccountId; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken.ServiceAccountTokenId; import org.elasticsearch.xpack.core.security.authc.support.Hasher; import org.elasticsearch.xpack.core.security.support.Validation; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; -import org.elasticsearch.xpack.security.authc.service.ServiceAccountToken.ServiceAccountTokenId; import org.elasticsearch.xpack.security.support.FileAttributesChecker; import java.nio.file.Path; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/IndexServiceAccountTokenStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/IndexServiceAccountTokenStore.java index 91e51340e7d8f..85c9b555b5352 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/IndexServiceAccountTokenStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/IndexServiceAccountTokenStore.java @@ -47,9 +47,10 @@ import org.elasticsearch.xpack.core.security.action.service.TokenInfo.TokenSource; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.Subject; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount.ServiceAccountId; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken.ServiceAccountTokenId; import org.elasticsearch.xpack.core.security.authc.support.Hasher; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; -import org.elasticsearch.xpack.security.authc.service.ServiceAccountToken.ServiceAccountTokenId; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; import org.elasticsearch.xpack.security.support.SecurityIndexManager; import org.elasticsearch.xpack.security.support.SecurityIndexManager.IndexState; @@ -80,6 +81,7 @@ public class IndexServiceAccountTokenStore extends CachingServiceAccountTokenSto private final ClusterService clusterService; private final Hasher hasher; + @SuppressWarnings("this-escape") public IndexServiceAccountTokenStore( Settings settings, ThreadPool threadPool, @@ -116,14 +118,14 @@ void doAuthenticate(ServiceAccountToken token, ActionListener { + readOnlyServiceAccountTokenStore.authenticate(serviceAccountToken, ActionListener.wrap(storeAuthenticationResult -> { if (storeAuthenticationResult.isSuccess()) { listener.onResponse( createAuthentication(account, serviceAccountToken, storeAuthenticationResult.getTokenSource(), nodeName) @@ -149,14 +153,23 @@ public void createIndexToken( CreateServiceAccountTokenRequest request, ActionListener listener ) { + if (indexServiceAccountTokenStore == null) { + throw new IllegalStateException("Can't create token because index service account token store not configured"); + } indexServiceAccountTokenStore.createToken(authentication, request, listener); } public void deleteIndexToken(DeleteServiceAccountTokenRequest request, ActionListener listener) { + if (indexServiceAccountTokenStore == null) { + throw new IllegalStateException("Can't delete token because index service account token store not configured"); + } indexServiceAccountTokenStore.deleteToken(request, listener); } public void findTokensFor(GetServiceAccountCredentialsRequest request, ActionListener listener) { + if (indexServiceAccountTokenStore == null) { + throw new IllegalStateException("Can't find tokens because index service account token store not configured"); + } final ServiceAccountId accountId = new ServiceAccountId(request.getNamespace(), request.getServiceName()); findIndexTokens(accountId, listener); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java index 40acb9a32f1bc..4eac3ddf85f1b 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.action.ActionModule; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.bulk.IncrementalBulkService; +import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; @@ -32,6 +33,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsModule; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.core.Nullable; import org.elasticsearch.core.TimeValue; import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; @@ -77,12 +79,15 @@ import org.elasticsearch.xpack.core.security.SecurityExtension; import org.elasticsearch.xpack.core.security.SecurityField; import org.elasticsearch.xpack.core.security.action.ActionTypes; +import org.elasticsearch.xpack.core.security.action.service.TokenInfo; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.AuthenticationTestHelper; import org.elasticsearch.xpack.core.security.authc.Realm; import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.file.FileRealmSettings; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountTokenStore; import org.elasticsearch.xpack.core.security.authc.support.CachingUsernamePasswordRealmSettings; import org.elasticsearch.xpack.core.security.authc.support.Hasher; import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl; @@ -99,6 +104,9 @@ import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm; import org.elasticsearch.xpack.security.authc.jwt.JwtRealm; import org.elasticsearch.xpack.security.authc.service.CachingServiceAccountTokenStore; +import org.elasticsearch.xpack.security.authc.service.FileServiceAccountTokenStore; +import org.elasticsearch.xpack.security.authc.service.IndexServiceAccountTokenStore; +import org.elasticsearch.xpack.security.authc.service.ServiceAccountService; import org.elasticsearch.xpack.security.operator.DefaultOperatorOnlyRegistry; import org.elasticsearch.xpack.security.operator.OperatorOnlyRegistry; import org.elasticsearch.xpack.security.operator.OperatorPrivileges; @@ -157,16 +165,34 @@ public class SecurityTests extends ESTestCase { private TestUtils.UpdatableLicenseState licenseState; public static class DummyExtension implements SecurityExtension { - private String realmType; + private final String realmType; + private final ServiceAccountTokenStore serviceAccountTokenStore; + private final String extensionName; DummyExtension(String realmType) { + this(realmType, "DummyExtension", null); + } + + DummyExtension(String realmType, String extensionName, @Nullable ServiceAccountTokenStore serviceAccountTokenStore) { this.realmType = realmType; + this.extensionName = extensionName; + this.serviceAccountTokenStore = serviceAccountTokenStore; + } + + @Override + public String extensionName() { + return extensionName; } @Override public Map getRealms(SecurityComponents components) { return Collections.singletonMap(realmType, config -> null); } + + @Override + public ServiceAccountTokenStore getServiceAccountTokenStore(SecurityComponents components) { + return serviceAccountTokenStore; + } } public static class DummyOperatorOnlyRegistry implements OperatorOnlyRegistry { @@ -266,7 +292,7 @@ public void testCustomRealmExtension() throws Exception { assertNotNull(realms.realmFactory("myrealm")); } - public void testCustomRealmExtensionConflict() throws Exception { + public void testCustomRealmExtensionConflict() { IllegalArgumentException e = expectThrows( IllegalArgumentException.class, () -> createComponents(Settings.EMPTY, new DummyExtension(FileRealmSettings.TYPE)) @@ -274,6 +300,64 @@ public void testCustomRealmExtensionConflict() throws Exception { assertEquals("Realm type [" + FileRealmSettings.TYPE + "] is already registered", e.getMessage()); } + public void testServiceAccountTokenStoreExtensionSuccess() throws Exception { + Collection components = createComponents( + Settings.EMPTY, + new DummyExtension( + "test_realm", + "DummyExtension", + (token, listener) -> listener.onResponse( + ServiceAccountTokenStore.StoreAuthenticationResult.successful(TokenInfo.TokenSource.FILE) + ) + ) + ); + ServiceAccountService serviceAccountService = findComponent(ServiceAccountService.class, components); + assertNotNull(serviceAccountService); + FileServiceAccountTokenStore fileServiceAccountTokenStore = findComponent(FileServiceAccountTokenStore.class, components); + assertNull(fileServiceAccountTokenStore); + IndexServiceAccountTokenStore indexServiceAccountTokenStore = findComponent(IndexServiceAccountTokenStore.class, components); + assertNull(indexServiceAccountTokenStore); + var account = randomFrom(ServiceAccountService.getServiceAccounts().values()); + assertThrows(IllegalStateException.class, () -> serviceAccountService.createIndexToken(null, null, null)); + var future = new PlainActionFuture(); + serviceAccountService.authenticateToken(ServiceAccountToken.newToken(account.id(), "test"), "test", future); + assertTrue(future.get().isServiceAccount()); + } + + public void testSeveralServiceAccountTokenStoreExtensionFail() { + IllegalStateException exception = assertThrows( + IllegalStateException.class, + () -> createComponents( + Settings.EMPTY, + new DummyExtension( + "test_realm_1", + "DummyExtension1", + (token, listener) -> listener.onResponse( + ServiceAccountTokenStore.StoreAuthenticationResult.successful(TokenInfo.TokenSource.FILE) + ) + ), + new DummyExtension( + "test_realm_2", + "DummyExtension2", + (token, listener) -> listener.onResponse( + ServiceAccountTokenStore.StoreAuthenticationResult.successful(TokenInfo.TokenSource.FILE) + ) + ) + ) + ); + assertThat(exception.getMessage(), containsString("More than one extension provided a ServiceAccountTokenStore override: ")); + } + + public void testNoServiceAccountTokenStoreExtension() throws Exception { + Collection components = createComponents(Settings.EMPTY); + ServiceAccountService serviceAccountService = findComponent(ServiceAccountService.class, components); + assertNotNull(serviceAccountService); + FileServiceAccountTokenStore fileServiceAccountTokenStore = findComponent(FileServiceAccountTokenStore.class, components); + assertNotNull(fileServiceAccountTokenStore); + IndexServiceAccountTokenStore indexServiceAccountTokenStore = findComponent(IndexServiceAccountTokenStore.class, components); + assertNotNull(indexServiceAccountTokenStore); + } + public void testAuditEnabled() throws Exception { Settings settings = Settings.builder().put(XPackSettings.AUDIT_ENABLED.getKey(), true).build(); Collection components = createComponents(settings); 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 638b1be718040..191b4840d733a 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 @@ -102,7 +102,9 @@ import org.elasticsearch.xpack.core.security.authc.AuthenticationTestHelper; import org.elasticsearch.xpack.core.security.authc.AuthenticationToken; import org.elasticsearch.xpack.core.security.authc.CrossClusterAccessSubjectInfo; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount.ServiceAccountId; import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountSettings; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; import org.elasticsearch.xpack.core.security.authc.support.mapper.TemplateRoleName; import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.ExpressionModel; import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.RoleMapperExpression; @@ -124,8 +126,6 @@ import org.elasticsearch.xpack.security.audit.AuditTrail; import org.elasticsearch.xpack.security.audit.AuditUtil; import org.elasticsearch.xpack.security.authc.ApiKeyService; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; -import org.elasticsearch.xpack.security.authc.service.ServiceAccountToken; import org.elasticsearch.xpack.security.rest.RemoteHostHeader; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; import org.elasticsearch.xpack.security.support.SecurityIndexManager; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java index 4bf840d281a8c..1b5bbd4de9a44 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java @@ -81,6 +81,7 @@ import org.elasticsearch.xpack.core.security.authc.RealmDomain; 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.service.ServiceAccountToken; import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer; import org.elasticsearch.xpack.core.security.authc.support.Hasher; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; @@ -98,7 +99,6 @@ import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm; import org.elasticsearch.xpack.security.authc.file.FileRealm; import org.elasticsearch.xpack.security.authc.service.ServiceAccountService; -import org.elasticsearch.xpack.security.authc.service.ServiceAccountToken; import org.elasticsearch.xpack.security.operator.OperatorPrivileges; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; import org.elasticsearch.xpack.security.support.SecurityIndexManager; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticatorChainTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticatorChainTests.java index 4517b639b7604..363593b83c9c4 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticatorChainTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticatorChainTests.java @@ -28,12 +28,12 @@ import org.elasticsearch.xpack.core.security.authc.AuthenticationTestHelper; import org.elasticsearch.xpack.core.security.authc.AuthenticationToken; import org.elasticsearch.xpack.core.security.authc.Realm; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer; import org.elasticsearch.xpack.core.security.authc.support.BearerToken; import org.elasticsearch.xpack.core.security.user.AnonymousUser; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.security.authc.ApiKeyService.ApiKeyCredentials; -import org.elasticsearch.xpack.security.authc.service.ServiceAccountToken; import org.elasticsearch.xpack.security.operator.OperatorPrivileges.OperatorPrivilegesService; import org.junit.Before; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ServiceAccountAuthenticatorTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ServiceAccountAuthenticatorTests.java index a16e8aecc5c67..5015c17380ff4 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ServiceAccountAuthenticatorTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ServiceAccountAuthenticatorTests.java @@ -16,10 +16,10 @@ import org.elasticsearch.telemetry.TestTelemetryPlugin; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.AuthenticationResult; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount.ServiceAccountId; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; import org.elasticsearch.xpack.core.security.user.User; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; import org.elasticsearch.xpack.security.authc.service.ServiceAccountService; -import org.elasticsearch.xpack.security.authc.service.ServiceAccountToken; import org.elasticsearch.xpack.security.metric.SecurityMetricType; import java.util.Map; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/CachingServiceAccountTokenStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/CachingServiceAccountTokenStoreTests.java index c09c7a1479131..c6f9a66d876db 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/CachingServiceAccountTokenStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/CachingServiceAccountTokenStoreTests.java @@ -17,9 +17,10 @@ import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xpack.core.security.action.service.TokenInfo.TokenSource; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount.ServiceAccountId; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountTokenStore.StoreAuthenticationResult; import org.elasticsearch.xpack.core.security.support.ValidationTests; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; -import org.elasticsearch.xpack.security.authc.service.ServiceAccountTokenStore.StoreAuthenticationResult; import org.junit.After; import org.junit.Before; @@ -34,6 +35,7 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class CachingServiceAccountTokenStoreTests extends ESTestCase { @@ -53,14 +55,22 @@ public void stop() { } } + private ServiceAccountToken newMockServiceAccountToken(ServiceAccountId accountId, String tokenName, SecureString secret) { + ServiceAccountToken serviceAccountToken = mock(ServiceAccountToken.class); + var serviceAccountTokenId = new ServiceAccountToken.ServiceAccountTokenId(accountId, tokenName); + when(serviceAccountToken.getQualifiedName()).thenReturn(serviceAccountTokenId.getQualifiedName()); + when(serviceAccountToken.getSecret()).thenReturn(secret); + return serviceAccountToken; + } + public void testCache() throws ExecutionException, InterruptedException { final ServiceAccountId accountId = new ServiceAccountId(randomAlphaOfLengthBetween(3, 8), randomAlphaOfLengthBetween(3, 8)); final SecureString validSecret = new SecureString("super-secret-value".toCharArray()); final SecureString invalidSecret = new SecureString("some-fishy-value".toCharArray()); - final ServiceAccountToken token1Valid = new ServiceAccountToken(accountId, "token1", validSecret); - final ServiceAccountToken token1Invalid = new ServiceAccountToken(accountId, "token1", invalidSecret); - final ServiceAccountToken token2Valid = new ServiceAccountToken(accountId, "token2", validSecret); - final ServiceAccountToken token2Invalid = new ServiceAccountToken(accountId, "token2", invalidSecret); + final ServiceAccountToken token1Valid = newMockServiceAccountToken(accountId, "token1", validSecret); + final ServiceAccountToken token1Invalid = newMockServiceAccountToken(accountId, "token1", invalidSecret); + final ServiceAccountToken token2Valid = newMockServiceAccountToken(accountId, "token2", validSecret); + final ServiceAccountToken token2Invalid = newMockServiceAccountToken(accountId, "token2", invalidSecret); final AtomicBoolean doAuthenticateInvoked = new AtomicBoolean(false); final TokenSource tokenSource = randomFrom(TokenSource.values()); @@ -68,7 +78,7 @@ public void testCache() throws ExecutionException, InterruptedException { @Override void doAuthenticate(ServiceAccountToken token, ActionListener listener) { doAuthenticateInvoked.set(true); - listener.onResponse(new StoreAuthenticationResult(validSecret.equals(token.getSecret()), getTokenSource())); + listener.onResponse(StoreAuthenticationResult.fromBooleanResult(getTokenSource(), validSecret.equals(token.getSecret()))); } @Override @@ -160,7 +170,7 @@ public void testCacheCanBeDisabled() throws ExecutionException, InterruptedExcep final CachingServiceAccountTokenStore store = new CachingServiceAccountTokenStore(settings, threadPool) { @Override void doAuthenticate(ServiceAccountToken token, ActionListener listener) { - listener.onResponse(new StoreAuthenticationResult(success, getTokenSource())); + listener.onResponse(StoreAuthenticationResult.fromBooleanResult(getTokenSource(), success)); } @Override @@ -181,7 +191,7 @@ public void testCacheInvalidateByKeys() { final CachingServiceAccountTokenStore store = new CachingServiceAccountTokenStore(globalSettings, threadPool) { @Override void doAuthenticate(ServiceAccountToken token, ActionListener listener) { - listener.onResponse(new StoreAuthenticationResult(true, getTokenSource())); + listener.onResponse(StoreAuthenticationResult.successful(getTokenSource())); } @Override diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/CompositeServiceAccountTokenStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/CompositeServiceAccountTokenStoreTests.java index 09cba2fdfaf6a..0b2d75bb32243 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/CompositeServiceAccountTokenStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/CompositeServiceAccountTokenStoreTests.java @@ -13,7 +13,9 @@ import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.security.action.service.TokenInfo.TokenSource; -import org.elasticsearch.xpack.security.authc.service.ServiceAccountTokenStore.StoreAuthenticationResult; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountTokenStore; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountTokenStore.StoreAuthenticationResult; import org.junit.Before; import org.mockito.Mockito; @@ -58,7 +60,7 @@ public void testAuthenticate() throws ExecutionException, InterruptedException { @SuppressWarnings("unchecked") final ActionListener listener = (ActionListener) invocationOnMock .getArguments()[1]; - listener.onResponse(new StoreAuthenticationResult(store1Success, tokenSource)); + listener.onResponse(StoreAuthenticationResult.fromBooleanResult(tokenSource, store1Success)); return null; }).when(store1).authenticate(eq(token), any()); @@ -66,7 +68,7 @@ public void testAuthenticate() throws ExecutionException, InterruptedException { @SuppressWarnings("unchecked") final ActionListener listener = (ActionListener) invocationOnMock .getArguments()[1]; - listener.onResponse(new StoreAuthenticationResult(store2Success, tokenSource)); + listener.onResponse(StoreAuthenticationResult.fromBooleanResult(tokenSource, store2Success)); return null; }).when(store2).authenticate(eq(token), any()); @@ -74,7 +76,7 @@ public void testAuthenticate() throws ExecutionException, InterruptedException { @SuppressWarnings("unchecked") final ActionListener listener = (ActionListener) invocationOnMock .getArguments()[1]; - listener.onResponse(new StoreAuthenticationResult(store3Success, tokenSource)); + listener.onResponse(StoreAuthenticationResult.fromBooleanResult(tokenSource, store3Success)); return null; }).when(store3).authenticate(eq(token), any()); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java index 21e29469bb02b..5ccefb8fbe134 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java @@ -56,6 +56,7 @@ import org.elasticsearch.xpack.core.security.action.user.PutUserAction; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.AuthenticationTestHelper; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache; import org.elasticsearch.xpack.core.security.authz.permission.Role; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/FileServiceAccountTokenStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/FileServiceAccountTokenStoreTests.java index 0f2a720660afd..e95a3e66ce04f 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/FileServiceAccountTokenStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/FileServiceAccountTokenStoreTests.java @@ -21,8 +21,8 @@ import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xpack.core.security.action.service.TokenInfo; import org.elasticsearch.xpack.core.security.audit.logfile.CapturingLogger; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount.ServiceAccountId; import org.elasticsearch.xpack.core.security.authc.support.Hasher; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; import org.junit.After; import org.junit.Before; @@ -238,7 +238,7 @@ public void testFindTokensFor() throws IOException { ); final ServiceAccountId accountId = new ServiceAccountId("elastic", "fleet-server"); - final List tokenInfos = store.findTokensFor(accountId); + final List tokenInfos = store.findNodeLocalTokensFor(accountId); assertThat(tokenInfos, hasSize(5)); assertThat( tokenInfos.stream().map(TokenInfo::getName).collect(Collectors.toUnmodifiableSet()), diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/IndexServiceAccountTokenStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/IndexServiceAccountTokenStoreTests.java index ec4b0059ed8a1..17249c9900ac9 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/IndexServiceAccountTokenStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/IndexServiceAccountTokenStoreTests.java @@ -55,10 +55,11 @@ import org.elasticsearch.xpack.core.security.action.service.TokenInfo.TokenSource; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.AuthenticationTestHelper; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount.ServiceAccountId; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountTokenStore.StoreAuthenticationResult; import org.elasticsearch.xpack.core.security.authc.support.Hasher; import org.elasticsearch.xpack.core.security.support.ValidationTests; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; -import org.elasticsearch.xpack.security.authc.service.ServiceAccountTokenStore.StoreAuthenticationResult; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; import org.elasticsearch.xpack.security.support.SecurityIndexManager; import org.junit.Before; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountIdTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountIdTests.java index ed6ac0f6de435..aa5baefa894a3 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountIdTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountIdTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount; import java.io.IOException; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountServiceTests.java index 43fe57dd8b313..44eb42d08ef59 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountServiceTests.java @@ -33,10 +33,12 @@ import org.elasticsearch.xpack.core.security.action.service.TokenInfo; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.AuthenticationTestHelper; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccount.ServiceAccountId; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountTokenStore; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.support.ValidationTests; import org.elasticsearch.xpack.core.security.user.User; -import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; import org.junit.After; import org.junit.Before; @@ -83,7 +85,14 @@ public void init() throws UnknownHostException { when(indexServiceAccountTokenStore.getTokenSource()).thenReturn(TokenInfo.TokenSource.INDEX); client = mock(Client.class); when(client.threadPool()).thenReturn(threadPool); - serviceAccountService = new ServiceAccountService(client, fileServiceAccountTokenStore, indexServiceAccountTokenStore); + serviceAccountService = new ServiceAccountService( + client, + new CompositeServiceAccountTokenStore( + List.of(fileServiceAccountTokenStore, indexServiceAccountTokenStore), + threadPool.getThreadContext() + ), + indexServiceAccountTokenStore + ); } @After @@ -228,16 +237,15 @@ public void testTryParseToken() throws IOException { List.of(magicBytes, (namespace + "/" + serviceName + "/" + tokenName + ":" + secret).getBytes(StandardCharsets.UTF_8)) ); final ServiceAccountToken serviceAccountToken1 = ServiceAccountService.tryParseToken(bearerString5); - final ServiceAccountToken serviceAccountToken2 = new ServiceAccountToken( - accountId, - tokenName, - new SecureString(secret.toCharArray()) - ); - assertThat(serviceAccountToken1, equalTo(serviceAccountToken2)); + + assertNotNull(serviceAccountToken1); + assertThat(serviceAccountToken1.getAccountId(), equalTo(accountId)); + assertThat(serviceAccountToken1.getTokenName(), equalTo(tokenName)); + assertThat(serviceAccountToken1.getSecret(), equalTo(new SecureString(secret.toCharArray()))); // Serialise and de-serialise service account token - final ServiceAccountToken parsedToken = ServiceAccountService.tryParseToken(serviceAccountToken2.asBearerString()); - assertThat(parsedToken, equalTo(serviceAccountToken2)); + final ServiceAccountToken parsedToken = ServiceAccountService.tryParseToken(serviceAccountToken1.asBearerString()); + assertThat(parsedToken, equalTo(serviceAccountToken1)); // Invalid magic byte satMockLog.addExpectation( @@ -295,25 +303,31 @@ public void testTryParseToken() throws IOException { ); sasMockLog.assertAllExpectationsMatched(); - // everything is fine - assertThat( - ServiceAccountService.tryParseToken( - new SecureString("AAEAAWVsYXN0aWMvZmxlZXQtc2VydmVyL3Rva2VuMTpzdXBlcnNlY3JldA".toCharArray()) - ), - equalTo( - new ServiceAccountToken( - new ServiceAccountId("elastic", "fleet-server"), - "token1", - new SecureString("supersecret".toCharArray()) - ) - ) + ServiceAccountToken parsedServiceAccountToken = ServiceAccountService.tryParseToken( + new SecureString("AAEAAWVsYXN0aWMvZmxlZXQtc2VydmVyL3Rva2VuMTpzdXBlcnNlY3JldA".toCharArray()) ); + + // everything is fine + assertNotNull(parsedServiceAccountToken); + assertThat(parsedServiceAccountToken.getAccountId(), equalTo(new ServiceAccountId("elastic", "fleet-server"))); + assertThat(parsedServiceAccountToken.getTokenName(), equalTo("token1")); + assertThat(parsedServiceAccountToken.getSecret(), equalTo(new SecureString("supersecret".toCharArray()))); } finally { Loggers.setLevel(satLogger, Level.INFO); Loggers.setLevel(sasLogger, Level.INFO); } } + private ServiceAccountToken newMockServiceAccountToken(ServiceAccountId accountId, String tokenName, SecureString secret) { + ServiceAccountToken serviceAccountToken = mock(ServiceAccountToken.class); + var serviceAccountTokenId = new ServiceAccountToken.ServiceAccountTokenId(accountId, tokenName); + when(serviceAccountToken.getQualifiedName()).thenReturn(serviceAccountTokenId.getQualifiedName()); + when(serviceAccountToken.getSecret()).thenReturn(secret); + when(serviceAccountToken.getAccountId()).thenReturn(accountId); + when(serviceAccountToken.getTokenName()).thenReturn(tokenName); + return serviceAccountToken; + } + public void testTryAuthenticateBearerToken() throws ExecutionException, InterruptedException { // Valid token final PlainActionFuture future5 = new PlainActionFuture<>(); @@ -325,7 +339,10 @@ public void testTryAuthenticateBearerToken() throws ExecutionException, Interrup final ActionListener listener = (ActionListener< ServiceAccountTokenStore.StoreAuthenticationResult>) invocationOnMock.getArguments()[1]; listener.onResponse( - new ServiceAccountTokenStore.StoreAuthenticationResult(store == authenticatingStore, store.getTokenSource()) + ServiceAccountTokenStore.StoreAuthenticationResult.fromBooleanResult( + store.getTokenSource(), + store == authenticatingStore + ) ); return null; }).when(store).authenticate(any(), any()); @@ -333,7 +350,7 @@ public void testTryAuthenticateBearerToken() throws ExecutionException, Interrup final String nodeName = randomAlphaOfLengthBetween(3, 8); serviceAccountService.authenticateToken( - new ServiceAccountToken( + newMockServiceAccountToken( new ServiceAccountId("elastic", "fleet-server"), "token1", new SecureString("super-secret-value".toCharArray()) @@ -379,7 +396,7 @@ public void testAuthenticateWithToken() throws ExecutionException, InterruptedEx ) ); final SecureString secret = new SecureString(randomAlphaOfLength(20).toCharArray()); - final ServiceAccountToken token1 = new ServiceAccountToken(accountId1, randomAlphaOfLengthBetween(3, 8), secret); + final ServiceAccountToken token1 = newMockServiceAccountToken(accountId1, randomAlphaOfLengthBetween(3, 8), secret); final PlainActionFuture future1 = new PlainActionFuture<>(); serviceAccountService.authenticateToken(token1, randomAlphaOfLengthBetween(3, 8), future1); final ExecutionException e1 = expectThrows(ExecutionException.class, future1::get); @@ -409,7 +426,7 @@ public void testAuthenticateWithToken() throws ExecutionException, InterruptedEx "the [" + accountId2.asPrincipal() + "] service account does not exist" ) ); - final ServiceAccountToken token2 = new ServiceAccountToken(accountId2, randomAlphaOfLengthBetween(3, 8), secret); + final ServiceAccountToken token2 = newMockServiceAccountToken(accountId2, randomAlphaOfLengthBetween(3, 8), secret); final PlainActionFuture future2 = new PlainActionFuture<>(); serviceAccountService.authenticateToken(token2, randomAlphaOfLengthBetween(3, 8), future2); final ExecutionException e2 = expectThrows(ExecutionException.class, future2::get); @@ -429,7 +446,7 @@ public void testAuthenticateWithToken() throws ExecutionException, InterruptedEx // Length of secret value is too short final ServiceAccountId accountId3 = new ServiceAccountId(ElasticServiceAccounts.NAMESPACE, "fleet-server"); final SecureString secret3 = new SecureString(randomAlphaOfLengthBetween(1, 9).toCharArray()); - final ServiceAccountToken token3 = new ServiceAccountToken(accountId3, randomAlphaOfLengthBetween(3, 8), secret3); + final ServiceAccountToken token3 = newMockServiceAccountToken(accountId3, randomAlphaOfLengthBetween(3, 8), secret3); mockLog.addExpectation( new MockLog.SeenEventExpectation( "secret value too short", @@ -456,7 +473,7 @@ public void testAuthenticateWithToken() throws ExecutionException, InterruptedEx ); mockLog.assertAllExpectationsMatched(); - final TokenInfo.TokenSource tokenSource = randomFrom(TokenInfo.TokenSource.values()); + final TokenInfo.TokenSource tokenSource = randomFrom(TokenInfo.TokenSource.FILE, TokenInfo.TokenSource.INDEX); final CachingServiceAccountTokenStore store; final CachingServiceAccountTokenStore otherStore; if (tokenSource == TokenInfo.TokenSource.FILE) { @@ -469,8 +486,8 @@ public void testAuthenticateWithToken() throws ExecutionException, InterruptedEx // Success based on credential store final ServiceAccountId accountId4 = new ServiceAccountId(ElasticServiceAccounts.NAMESPACE, "fleet-server"); - final ServiceAccountToken token4 = new ServiceAccountToken(accountId4, randomAlphaOfLengthBetween(3, 8), secret); - final ServiceAccountToken token5 = new ServiceAccountToken( + final ServiceAccountToken token4 = newMockServiceAccountToken(accountId4, randomAlphaOfLengthBetween(3, 8), secret); + final ServiceAccountToken token5 = newMockServiceAccountToken( accountId4, randomAlphaOfLengthBetween(3, 8), new SecureString(randomAlphaOfLength(20).toCharArray()) @@ -480,7 +497,7 @@ public void testAuthenticateWithToken() throws ExecutionException, InterruptedEx @SuppressWarnings("unchecked") final ActionListener listener = (ActionListener< ServiceAccountTokenStore.StoreAuthenticationResult>) invocationOnMock.getArguments()[1]; - listener.onResponse(new ServiceAccountTokenStore.StoreAuthenticationResult(true, store.getTokenSource())); + listener.onResponse(ServiceAccountTokenStore.StoreAuthenticationResult.successful(store.getTokenSource())); return null; }).when(store).authenticate(eq(token4), any()); @@ -488,7 +505,7 @@ public void testAuthenticateWithToken() throws ExecutionException, InterruptedEx @SuppressWarnings("unchecked") final ActionListener listener = (ActionListener< ServiceAccountTokenStore.StoreAuthenticationResult>) invocationOnMock.getArguments()[1]; - listener.onResponse(new ServiceAccountTokenStore.StoreAuthenticationResult(false, store.getTokenSource())); + listener.onResponse(ServiceAccountTokenStore.StoreAuthenticationResult.failed(store.getTokenSource())); return null; }).when(store).authenticate(eq(token5), any()); @@ -496,7 +513,7 @@ public void testAuthenticateWithToken() throws ExecutionException, InterruptedEx @SuppressWarnings("unchecked") final ActionListener listener = (ActionListener< ServiceAccountTokenStore.StoreAuthenticationResult>) invocationOnMock.getArguments()[1]; - listener.onResponse(new ServiceAccountTokenStore.StoreAuthenticationResult(false, otherStore.getTokenSource())); + listener.onResponse(ServiceAccountTokenStore.StoreAuthenticationResult.failed(otherStore.getTokenSource())); return null; }).when(otherStore).authenticate(any(), any()); diff --git a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/service/FileTokensToolTests.java b/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/service/FileTokensToolTests.java index 24373e5061206..4084026b4cdbc 100644 --- a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/service/FileTokensToolTests.java +++ b/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/service/FileTokensToolTests.java @@ -22,10 +22,10 @@ import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.PathUtilsForTesting; import org.elasticsearch.env.Environment; +import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountToken.ServiceAccountTokenId; import org.elasticsearch.xpack.core.security.authc.support.Hasher; import org.elasticsearch.xpack.core.security.support.Validation; import org.elasticsearch.xpack.core.security.support.ValidationTests; -import org.elasticsearch.xpack.security.authc.service.ServiceAccountToken.ServiceAccountTokenId; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass;