Skip to content

Commit c369f97

Browse files
authored
Add extensionName() to security extension (#79329)
Extension loading code needs to know how to refer to an extension at runtime. It previously used "toString()", but there was no contract that required that this method be implemented in a meaningful way. A new extensionName() method is added which defaults to the class name of the extension, but can be customized by implementations
1 parent 4ea349d commit c369f97

File tree

3 files changed

+56
-30
lines changed

3 files changed

+56
-30
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityExtension.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,8 @@ default AuthenticationFailureHandler getAuthenticationFailureHandler(SecurityCom
115115
default AuthorizationEngine getAuthorizationEngine(Settings settings) {
116116
return null;
117117
}
118+
119+
default String extensionName() {
120+
return getClass().getName();
121+
}
118122
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343
import org.elasticsearch.common.util.concurrent.EsExecutors;
4444
import org.elasticsearch.common.util.concurrent.ThreadContext;
4545
import org.elasticsearch.common.util.set.Sets;
46+
import org.elasticsearch.core.Nullable;
47+
import org.elasticsearch.xcontent.NamedXContentRegistry;
48+
import org.elasticsearch.xcontent.XContentBuilder;
4649
import org.elasticsearch.env.Environment;
4750
import org.elasticsearch.env.NodeEnvironment;
4851
import org.elasticsearch.http.HttpServerTransport;
@@ -567,7 +570,7 @@ Collection<Object> createComponents(Client client, ThreadPool threadPool, Cluste
567570
extensionComponents
568571
);
569572
if (providers != null && providers.isEmpty() == false) {
570-
customRoleProviders.put(extension.toString(), providers);
573+
customRoleProviders.put(extension.extensionName(), providers);
571574
}
572575
}
573576

@@ -676,37 +679,15 @@ auditTrailService, failureHandler, threadPool, anonymousUser, getAuthorizationEn
676679
}
677680

678681
private AuthorizationEngine getAuthorizationEngine() {
679-
AuthorizationEngine authorizationEngine = null;
680-
String extensionName = null;
681-
for (SecurityExtension extension : securityExtensions) {
682-
final AuthorizationEngine extensionEngine = extension.getAuthorizationEngine(settings);
683-
if (extensionEngine != null && authorizationEngine != null) {
684-
throw new IllegalStateException("Extensions [" + extensionName + "] and [" + extension.toString() + "] "
685-
+ "both set an authorization engine");
686-
}
687-
authorizationEngine = extensionEngine;
688-
extensionName = extension.toString();
689-
}
690-
691-
if (authorizationEngine != null) {
692-
logger.debug("Using authorization engine from extension [" + extensionName + "]");
693-
}
694-
return authorizationEngine;
682+
return findValueFromExtensions("authorization engine", extension -> extension.getAuthorizationEngine(settings));
695683
}
696684

697685
private AuthenticationFailureHandler createAuthenticationFailureHandler(final Realms realms,
698686
final SecurityExtension.SecurityComponents components) {
699-
AuthenticationFailureHandler failureHandler = null;
700-
String extensionName = null;
701-
for (SecurityExtension extension : securityExtensions) {
702-
AuthenticationFailureHandler extensionFailureHandler = extension.getAuthenticationFailureHandler(components);
703-
if (extensionFailureHandler != null && failureHandler != null) {
704-
throw new IllegalStateException("Extensions [" + extensionName + "] and [" + extension.toString() + "] "
705-
+ "both set an authentication failure handler");
706-
}
707-
failureHandler = extensionFailureHandler;
708-
extensionName = extension.toString();
709-
}
687+
AuthenticationFailureHandler failureHandler = findValueFromExtensions(
688+
"authentication failure handler",
689+
extension -> extension.getAuthenticationFailureHandler(components)
690+
);
710691
if (failureHandler == null) {
711692
logger.debug("Using default authentication failure handler");
712693
Supplier<Map<String, List<String>>> headersSupplier = () -> {
@@ -743,12 +724,48 @@ private AuthenticationFailureHandler createAuthenticationFailureHandler(final Re
743724
getLicenseState().addListener(() -> {
744725
finalDefaultFailureHandler.setHeaders(headersSupplier.get());
745726
});
746-
} else {
747-
logger.debug("Using authentication failure handler from extension [" + extensionName + "]");
748727
}
749728
return failureHandler;
750729
}
751730

731+
/**
732+
* Calls the provided function for each configured extension and return the value that was generated by the extensions.
733+
* If multiple extensions provide a value, throws {@link IllegalStateException}.
734+
* If no extensions provide a value (or if there are no extensions) returns {@code null}.
735+
*/
736+
@Nullable
737+
private <T> T findValueFromExtensions(String valueType, Function<SecurityExtension, T> method) {
738+
T foundValue = null;
739+
String fromExtension = null;
740+
for (SecurityExtension extension : securityExtensions) {
741+
final T extensionValue = method.apply(extension);
742+
if (extensionValue == null) {
743+
continue;
744+
}
745+
if (foundValue == null) {
746+
foundValue = extensionValue;
747+
fromExtension = extension.extensionName();
748+
} else {
749+
throw new IllegalStateException(
750+
"Extensions ["
751+
+ fromExtension
752+
+ "] and ["
753+
+ extension.extensionName()
754+
+ "] "
755+
+ " both attempted to provide a value for ["
756+
+ valueType
757+
+ "]"
758+
);
759+
}
760+
}
761+
if (foundValue == null) {
762+
return null;
763+
} else {
764+
logger.debug("Using [{}] [{}] from extension [{}]", valueType, foundValue, fromExtension);
765+
return foundValue;
766+
}
767+
}
768+
752769
@Override
753770
public Settings additionalSettings() {
754771
return additionalSettings(settings, enabled);

x-pack/qa/security-example-spi-extension/src/main/java/org/elasticsearch/example/ExampleSecurityExtension.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ public class ExampleSecurityExtension implements SecurityExtension {
4242
});
4343
}
4444

45+
@Override
46+
public String extensionName() {
47+
return "example";
48+
}
49+
4550
@Override
4651
public Map<String, Realm.Factory> getRealms(SecurityComponents components) {
4752
return Map.ofEntries(

0 commit comments

Comments
 (0)