Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
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.apikey.CustomApiKeyAuthenticator;
import org.elasticsearch.xpack.core.security.authc.apikey.CustomAuthenticator;
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;
Expand Down Expand Up @@ -129,7 +129,7 @@ default ServiceAccountTokenStore getServiceAccountTokenStore(SecurityComponents
return null;
}

default CustomApiKeyAuthenticator getCustomApiKeyAuthenticator(SecurityComponents components) {
default List<CustomAuthenticator> getCustomAuthenticators(SecurityComponents components) {
return null;
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* 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.apikey;

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.AuthenticationResult;
import org.elasticsearch.xpack.core.security.authc.AuthenticationToken;

/**
* An extension point to provide a custom authenticator implementation. For example, a custom API key or a custom OAuth2
* token implementation. The implementation is wrapped by a core `Authenticator` class and included in the authenticator chain
* _before_ the respective "standard" authenticator(s).
*/
public interface CustomAuthenticator {

boolean supports(AuthenticationToken token);

@Nullable
AuthenticationToken extractToken(ThreadContext context);

void authenticate(@Nullable AuthenticationToken token, ActionListener<AuthenticationResult<Authentication>> listener);

}
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@
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.apikey.CustomApiKeyAuthenticator;
import org.elasticsearch.xpack.core.security.authc.apikey.CustomAuthenticator;
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;
Expand Down Expand Up @@ -1079,9 +1079,7 @@ Collection<Object> createComponents(
operatorPrivilegesService.set(OperatorPrivileges.NOOP_OPERATOR_PRIVILEGES_SERVICE);
}

final CustomApiKeyAuthenticator customApiKeyAuthenticator = createCustomApiKeyAuthenticator(extensionComponents);

components.add(customApiKeyAuthenticator);
final List<CustomAuthenticator> customAuthenticators = getCustomAuthenticatorFromExtensions(extensionComponents);

authcService.set(
new AuthenticationService(
Expand All @@ -1095,7 +1093,7 @@ Collection<Object> createComponents(
apiKeyService,
serviceAccountService,
operatorPrivilegesService.get(),
customApiKeyAuthenticator,
customAuthenticators,
telemetryProvider.getMeterRegistry()
)
);
Expand Down Expand Up @@ -1231,45 +1229,48 @@ Collection<Object> createComponents(
return components;
}

private CustomApiKeyAuthenticator createCustomApiKeyAuthenticator(SecurityExtension.SecurityComponents extensionComponents) {
final Map<String, CustomApiKeyAuthenticator> customApiKeyAuthenticatorByExtension = new HashMap<>();
for (final SecurityExtension extension : securityExtensions) {
final CustomApiKeyAuthenticator customApiKeyAuthenticator = extension.getCustomApiKeyAuthenticator(extensionComponents);
if (customApiKeyAuthenticator != null) {
if (false == isInternalExtension(extension)) {
private List<CustomAuthenticator> getCustomAuthenticatorFromExtensions(SecurityExtension.SecurityComponents extensionComponents) {
final Map<String, List<CustomAuthenticator>> customAuthenticatorsByExtension = new HashMap<>();
for (final SecurityExtension securityExtension : securityExtensions) {
final List<CustomAuthenticator> customAuthenticators = securityExtension.getCustomAuthenticators(extensionComponents);
if (customAuthenticators != null) {
if (false == isInternalExtension(securityExtension)) {
throw new IllegalStateException(
"The ["
+ extension.extensionName()
+ "] extension tried to install a custom CustomApiKeyAuthenticator. "
+ securityExtension.extensionName()
+ "] extension tried to install a "
+ CustomAuthenticator.class.getSimpleName()
+ ". "
+ "This functionality is not available to external extensions."
);
}
customApiKeyAuthenticatorByExtension.put(extension.extensionName(), customApiKeyAuthenticator);
customAuthenticatorsByExtension.put(securityExtension.extensionName(), customAuthenticators);
}
}

if (customApiKeyAuthenticatorByExtension.isEmpty()) {
if (customAuthenticatorsByExtension.isEmpty()) {
logger.debug(
"No custom implementation for [{}]. Falling-back to noop implementation.",
CustomApiKeyAuthenticator.class.getCanonicalName()
"No custom implementations for [{}] provided by security extensions.",
CustomAuthenticator.class.getCanonicalName()
);
return new CustomApiKeyAuthenticator.Noop();

} else if (customApiKeyAuthenticatorByExtension.size() > 1) {
return List.of();
} else if (customAuthenticatorsByExtension.size() > 1) {
throw new IllegalStateException(
"Multiple extensions tried to install a custom CustomApiKeyAuthenticator: " + customApiKeyAuthenticatorByExtension.keySet()
"Multiple extensions tried to install custom authenticators: " + customAuthenticatorsByExtension.keySet()
);

} else {
final var authenticatorByExtensionEntry = customApiKeyAuthenticatorByExtension.entrySet().iterator().next();
final CustomApiKeyAuthenticator customApiKeyAuthenticator = authenticatorByExtensionEntry.getValue();
final var authenticatorByExtensionEntry = customAuthenticatorsByExtension.entrySet().iterator().next();
final List<CustomAuthenticator> customAuthenticators = authenticatorByExtensionEntry.getValue();
final String extensionName = authenticatorByExtensionEntry.getKey();
logger.debug(
"CustomApiKeyAuthenticator implementation [{}] provided by extension [{}]",
customApiKeyAuthenticator.getClass().getCanonicalName(),
extensionName
);
return customApiKeyAuthenticator;
for (CustomAuthenticator authenticator : customAuthenticators) {
logger.debug(
"{} implementation [{}] provided by extension [{}]",
CustomAuthenticator.class.getSimpleName(),
authenticator.getClass().getCanonicalName(),
extensionName
);
}
return customAuthenticators;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import org.elasticsearch.xpack.core.security.authc.AuthenticationServiceField;
import org.elasticsearch.xpack.core.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.core.security.authc.Realm;
import org.elasticsearch.xpack.core.security.authc.apikey.CustomApiKeyAuthenticator;
import org.elasticsearch.xpack.core.security.authc.apikey.CustomAuthenticator;
import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer;
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.EmptyAuthorizationInfo;
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
Expand Down Expand Up @@ -93,7 +93,7 @@ public AuthenticationService(
ApiKeyService apiKeyService,
ServiceAccountService serviceAccountService,
OperatorPrivilegesService operatorPrivilegesService,
CustomApiKeyAuthenticator customApiKeyAuthenticator,
List<CustomAuthenticator> customAuthenticators,
MeterRegistry meterRegistry
) {
this.realms = realms;
Expand All @@ -110,14 +110,15 @@ public AuthenticationService(
}

final String nodeName = Node.NODE_NAME_SETTING.get(settings);

this.authenticatorChain = new AuthenticatorChain(
settings,
operatorPrivilegesService,
anonymousUser,
new AuthenticationContextSerializer(),
new PluggableAuthenticatorChain(customAuthenticators),
new ServiceAccountAuthenticator(serviceAccountService, nodeName, meterRegistry),
new OAuth2TokenAuthenticator(tokenService, meterRegistry),
new PluggableApiKeyAuthenticator(customApiKeyAuthenticator),
new ApiKeyAuthenticator(apiKeyService, nodeName, meterRegistry),
new RealmsAuthenticator(numInvalidation, lastSuccessfulAuthCache, meterRegistry)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public interface Authenticator {
/**
* Attempt to Extract an {@link AuthenticationToken} from the given {@link Context}.
* @param context The context object encapsulating current request and other information relevant for authentication.
*

* @return An {@link AuthenticationToken} if one can be extracted or null if this Authenticator cannot
* extract one.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.operator.OperatorPrivileges.OperatorPrivilegesService;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
Expand All @@ -52,9 +54,9 @@ class AuthenticatorChain {
OperatorPrivilegesService operatorPrivilegesService,
AnonymousUser anonymousUser,
AuthenticationContextSerializer authenticationSerializer,
PluggableAuthenticatorChain pluggableAuthenticatorChain,
ServiceAccountAuthenticator serviceAccountAuthenticator,
OAuth2TokenAuthenticator oAuth2TokenAuthenticator,
PluggableApiKeyAuthenticator pluggableApiKeyAuthenticator,
ApiKeyAuthenticator apiKeyAuthenticator,
RealmsAuthenticator realmsAuthenticator
) {
Expand All @@ -65,13 +67,16 @@ class AuthenticatorChain {
this.isAnonymousUserEnabled = AnonymousUser.isAnonymousEnabled(settings);
this.authenticationSerializer = authenticationSerializer;
this.realmsAuthenticator = realmsAuthenticator;
this.allAuthenticators = List.of(
serviceAccountAuthenticator,
oAuth2TokenAuthenticator,
pluggableApiKeyAuthenticator,
apiKeyAuthenticator,
realmsAuthenticator
);

List<Authenticator> authenticators = new ArrayList<>();
if (pluggableAuthenticatorChain.hasCustomAuthenticators()) {
authenticators.add(pluggableAuthenticatorChain);
}
authenticators.add(serviceAccountAuthenticator);
authenticators.add(oAuth2TokenAuthenticator);
authenticators.add(apiKeyAuthenticator);
authenticators.add(realmsAuthenticator);
this.allAuthenticators = Collections.unmodifiableList(authenticators);
}

void authenticate(Authenticator.Context context, ActionListener<Authentication> originalListener) {
Expand Down

This file was deleted.

Loading