|
@@ -18,10 +18,10 @@ import org.elasticsearch.rest.RestStatus;
|
|
|
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.service.DeleteServiceAccountTokenRequest;
|
|
|
+import org.elasticsearch.xpack.core.security.action.service.GetServiceAccountCredentialsNodesRequest;
|
|
|
import org.elasticsearch.xpack.core.security.action.service.GetServiceAccountCredentialsRequest;
|
|
|
import org.elasticsearch.xpack.core.security.action.service.GetServiceAccountCredentialsResponse;
|
|
|
import org.elasticsearch.xpack.core.security.action.service.GetServiceAccountNodesCredentialsAction;
|
|
|
-import org.elasticsearch.xpack.core.security.action.service.GetServiceAccountCredentialsNodesRequest;
|
|
|
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.Authentication;
|
|
@@ -29,7 +29,6 @@ import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountSetting
|
|
|
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
|
|
|
import org.elasticsearch.xpack.core.security.user.User;
|
|
|
import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId;
|
|
|
-import org.elasticsearch.xpack.security.authc.support.HttpTlsRuntimeCheck;
|
|
|
|
|
|
import java.util.Collection;
|
|
|
import java.util.List;
|
|
@@ -50,17 +49,14 @@ public class ServiceAccountService {
|
|
|
private final Client client;
|
|
|
private final IndexServiceAccountTokenStore indexServiceAccountTokenStore;
|
|
|
private final CompositeServiceAccountTokenStore compositeServiceAccountTokenStore;
|
|
|
- private final HttpTlsRuntimeCheck httpTlsRuntimeCheck;
|
|
|
|
|
|
public ServiceAccountService(Client client,
|
|
|
FileServiceAccountTokenStore fileServiceAccountTokenStore,
|
|
|
- IndexServiceAccountTokenStore indexServiceAccountTokenStore,
|
|
|
- HttpTlsRuntimeCheck httpTlsRuntimeCheck) {
|
|
|
+ IndexServiceAccountTokenStore indexServiceAccountTokenStore) {
|
|
|
this.client = client;
|
|
|
this.indexServiceAccountTokenStore = indexServiceAccountTokenStore;
|
|
|
this.compositeServiceAccountTokenStore = new CompositeServiceAccountTokenStore(
|
|
|
List.of(fileServiceAccountTokenStore, indexServiceAccountTokenStore), client.threadPool().getThreadContext());
|
|
|
- this.httpTlsRuntimeCheck = httpTlsRuntimeCheck;
|
|
|
}
|
|
|
|
|
|
public static boolean isServiceAccountPrincipal(String principal) {
|
|
@@ -103,79 +99,76 @@ public class ServiceAccountService {
|
|
|
|
|
|
public void authenticateToken(ServiceAccountToken serviceAccountToken, String nodeName, ActionListener<Authentication> listener) {
|
|
|
logger.trace("attempt to authenticate service account token [{}]", serviceAccountToken.getQualifiedName());
|
|
|
- httpTlsRuntimeCheck.checkTlsThenExecute(listener::onFailure, "service account authentication", () -> {
|
|
|
- if (ElasticServiceAccounts.NAMESPACE.equals(serviceAccountToken.getAccountId().namespace()) == false) {
|
|
|
- logger.debug("only [{}] service accounts are supported, but received [{}]",
|
|
|
- ElasticServiceAccounts.NAMESPACE, serviceAccountToken.getAccountId().asPrincipal());
|
|
|
- listener.onFailure(createAuthenticationException(serviceAccountToken));
|
|
|
- return;
|
|
|
- }
|
|
|
|
|
|
- final ServiceAccount account = ACCOUNTS.get(serviceAccountToken.getAccountId().asPrincipal());
|
|
|
- if (account == null) {
|
|
|
- logger.debug("the [{}] service account does not exist", serviceAccountToken.getAccountId().asPrincipal());
|
|
|
- listener.onFailure(createAuthenticationException(serviceAccountToken));
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (ElasticServiceAccounts.NAMESPACE.equals(serviceAccountToken.getAccountId().namespace()) == false) {
|
|
|
+ logger.debug(
|
|
|
+ "only [{}] service accounts are supported, but received [{}]",
|
|
|
+ ElasticServiceAccounts.NAMESPACE,
|
|
|
+ serviceAccountToken.getAccountId().asPrincipal()
|
|
|
+ );
|
|
|
+ listener.onFailure(createAuthenticationException(serviceAccountToken));
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- if (serviceAccountToken.getSecret().length() < MIN_TOKEN_SECRET_LENGTH) {
|
|
|
- logger.debug("failing authentication for service account token [{}],"
|
|
|
- + " the provided credential has length [{}]"
|
|
|
- + " but a token's secret value must be at least [{}] characters",
|
|
|
- serviceAccountToken.getQualifiedName(),
|
|
|
- serviceAccountToken.getSecret().length(),
|
|
|
- MIN_TOKEN_SECRET_LENGTH);
|
|
|
- listener.onFailure(createAuthenticationException(serviceAccountToken));
|
|
|
- return;
|
|
|
- }
|
|
|
+ final ServiceAccount account = ACCOUNTS.get(serviceAccountToken.getAccountId().asPrincipal());
|
|
|
+ if (account == null) {
|
|
|
+ logger.debug("the [{}] service account does not exist", serviceAccountToken.getAccountId().asPrincipal());
|
|
|
+ listener.onFailure(createAuthenticationException(serviceAccountToken));
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- compositeServiceAccountTokenStore.authenticate(serviceAccountToken, ActionListener.wrap(storeAuthenticationResult -> {
|
|
|
- if (storeAuthenticationResult.isSuccess()) {
|
|
|
- listener.onResponse(
|
|
|
- createAuthentication(account, serviceAccountToken, storeAuthenticationResult.getTokenSource() , nodeName));
|
|
|
- } else {
|
|
|
- final ElasticsearchSecurityException e = createAuthenticationException(serviceAccountToken);
|
|
|
- logger.debug(e.getMessage());
|
|
|
- listener.onFailure(e);
|
|
|
- }
|
|
|
- }, listener::onFailure));
|
|
|
- });
|
|
|
+ if (serviceAccountToken.getSecret().length() < MIN_TOKEN_SECRET_LENGTH) {
|
|
|
+ logger.debug(
|
|
|
+ "failing authentication for service account token [{}],"
|
|
|
+ + " the provided credential has length [{}]"
|
|
|
+ + " but a token's secret value must be at least [{}] characters",
|
|
|
+ serviceAccountToken.getQualifiedName(),
|
|
|
+ serviceAccountToken.getSecret().length(),
|
|
|
+ MIN_TOKEN_SECRET_LENGTH
|
|
|
+ );
|
|
|
+ listener.onFailure(createAuthenticationException(serviceAccountToken));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ compositeServiceAccountTokenStore.authenticate(serviceAccountToken, ActionListener.wrap(storeAuthenticationResult -> {
|
|
|
+ if (storeAuthenticationResult.isSuccess()) {
|
|
|
+ listener.onResponse(
|
|
|
+ createAuthentication(account, serviceAccountToken, storeAuthenticationResult.getTokenSource(), nodeName)
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ final ElasticsearchSecurityException e = createAuthenticationException(serviceAccountToken);
|
|
|
+ logger.debug(e.getMessage());
|
|
|
+ listener.onFailure(e);
|
|
|
+ }
|
|
|
+ }, listener::onFailure));
|
|
|
}
|
|
|
|
|
|
public void createIndexToken(Authentication authentication, CreateServiceAccountTokenRequest request,
|
|
|
ActionListener<CreateServiceAccountTokenResponse> listener) {
|
|
|
- httpTlsRuntimeCheck.checkTlsThenExecute(listener::onFailure,
|
|
|
- "create index-backed service token",
|
|
|
- () -> indexServiceAccountTokenStore.createToken(authentication, request, listener));
|
|
|
+ indexServiceAccountTokenStore.createToken(authentication, request, listener);
|
|
|
}
|
|
|
|
|
|
public void deleteIndexToken(DeleteServiceAccountTokenRequest request, ActionListener<Boolean> listener) {
|
|
|
- httpTlsRuntimeCheck.checkTlsThenExecute(
|
|
|
- listener::onFailure,
|
|
|
- "delete index-backed service token",
|
|
|
- () -> indexServiceAccountTokenStore.deleteToken(request, listener));
|
|
|
+ indexServiceAccountTokenStore.deleteToken(request, listener);
|
|
|
}
|
|
|
|
|
|
public void findTokensFor(GetServiceAccountCredentialsRequest request,
|
|
|
ActionListener<GetServiceAccountCredentialsResponse> listener) {
|
|
|
- httpTlsRuntimeCheck.checkTlsThenExecute(listener::onFailure, "find service tokens", () -> {
|
|
|
- final ServiceAccountId accountId = new ServiceAccountId(request.getNamespace(), request.getServiceName());
|
|
|
- findIndexTokens(accountId, listener);
|
|
|
- });
|
|
|
+ final ServiceAccountId accountId = new ServiceAccountId(request.getNamespace(), request.getServiceName());
|
|
|
+ findIndexTokens(accountId, listener);
|
|
|
}
|
|
|
|
|
|
public void getRoleDescriptor(Authentication authentication, ActionListener<RoleDescriptor> listener) {
|
|
|
assert authentication.isServiceAccount() : "authentication is not for service account: " + authentication;
|
|
|
- httpTlsRuntimeCheck.checkTlsThenExecute(listener::onFailure, "service account role descriptor resolving", () -> {
|
|
|
- final String principal = authentication.getUser().principal();
|
|
|
- final ServiceAccount account = ACCOUNTS.get(principal);
|
|
|
- if (account == null) {
|
|
|
- listener.onFailure(new ElasticsearchSecurityException(
|
|
|
- "cannot load role for service account [" + principal + "] - no such service account"));
|
|
|
- return;
|
|
|
- }
|
|
|
- listener.onResponse(account.roleDescriptor());
|
|
|
- });
|
|
|
+ final String principal = authentication.getUser().principal();
|
|
|
+ final ServiceAccount account = ACCOUNTS.get(principal);
|
|
|
+ if (account == null) {
|
|
|
+ listener.onFailure(
|
|
|
+ new ElasticsearchSecurityException("cannot load role for service account [" + principal + "] - no such service account")
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ listener.onResponse(account.roleDescriptor());
|
|
|
}
|
|
|
|
|
|
private Authentication createAuthentication(ServiceAccount account, ServiceAccountToken token, TokenSource tokenSource,
|