[Entitlements] Differentiate between ES modules and plugins (external) (#117973)

This commit is contained in:
Lorenzo Dematté 2024-12-12 13:10:56 +01:00 committed by GitHub
parent b2998378a3
commit f3dc0bdd50
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 96 additions and 57 deletions

View file

@ -13,8 +13,8 @@ apply plugin: 'elasticsearch.internal-test-artifact'
dependencies { dependencies {
javaRestTestImplementation project(':libs:entitlement:qa:common') javaRestTestImplementation project(':libs:entitlement:qa:common')
clusterPlugins project(':libs:entitlement:qa:entitlement-allowed') clusterModules project(':libs:entitlement:qa:entitlement-allowed')
clusterPlugins project(':libs:entitlement:qa:entitlement-allowed-nonmodular') clusterModules project(':libs:entitlement:qa:entitlement-allowed-nonmodular')
clusterPlugins project(':libs:entitlement:qa:entitlement-denied') clusterPlugins project(':libs:entitlement:qa:entitlement-denied')
clusterPlugins project(':libs:entitlement:qa:entitlement-denied-nonmodular') clusterPlugins project(':libs:entitlement:qa:entitlement-denied-nonmodular')
} }

View file

@ -28,8 +28,8 @@ public class EntitlementsAllowedIT extends ESRestTestCase {
@ClassRule @ClassRule
public static ElasticsearchCluster cluster = ElasticsearchCluster.local() public static ElasticsearchCluster cluster = ElasticsearchCluster.local()
.plugin("entitlement-allowed") .module("entitlement-allowed")
.plugin("entitlement-allowed-nonmodular") .module("entitlement-allowed-nonmodular")
.systemProperty("es.entitlements.enabled", "true") .systemProperty("es.entitlements.enabled", "true")
.setting("xpack.security.enabled", "false") .setting("xpack.security.enabled", "false")
.build(); .build();

View file

@ -15,7 +15,6 @@ import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine; import com.sun.tools.attach.VirtualMachine;
import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.entitlement.initialization.EntitlementInitialization; import org.elasticsearch.entitlement.initialization.EntitlementInitialization;
import org.elasticsearch.logging.LogManager; import org.elasticsearch.logging.LogManager;
import org.elasticsearch.logging.Logger; import org.elasticsearch.logging.Logger;
@ -29,7 +28,9 @@ import java.util.function.Function;
public class EntitlementBootstrap { public class EntitlementBootstrap {
public record BootstrapArgs(Collection<Tuple<Path, Boolean>> pluginData, Function<Class<?>, String> pluginResolver) {} public record PluginData(Path pluginPath, boolean isModular, boolean isExternalPlugin) {}
public record BootstrapArgs(Collection<PluginData> pluginData, Function<Class<?>, String> pluginResolver) {}
private static BootstrapArgs bootstrapArgs; private static BootstrapArgs bootstrapArgs;
@ -40,11 +41,11 @@ public class EntitlementBootstrap {
/** /**
* Activates entitlement checking. Once this method returns, calls to methods protected by Entitlements from classes without a valid * Activates entitlement checking. Once this method returns, calls to methods protected by Entitlements from classes without a valid
* policy will throw {@link org.elasticsearch.entitlement.runtime.api.NotEntitledException}. * policy will throw {@link org.elasticsearch.entitlement.runtime.api.NotEntitledException}.
* @param pluginData a collection of (plugin path, boolean), that holds the paths of all the installed Elasticsearch modules and * @param pluginData a collection of (plugin path, boolean, boolean), that holds the paths of all the installed Elasticsearch modules
* plugins, and whether they are Java modular or not. * and plugins, whether they are Java modular or not, and whether they are Elasticsearch modules or external plugins.
* @param pluginResolver a functor to map a Java Class to the plugin it belongs to (the plugin name). * @param pluginResolver a functor to map a Java Class to the plugin it belongs to (the plugin name).
*/ */
public static void bootstrap(Collection<Tuple<Path, Boolean>> pluginData, Function<Class<?>, String> pluginResolver) { public static void bootstrap(Collection<PluginData> pluginData, Function<Class<?>, String> pluginResolver) {
logger.debug("Loading entitlement agent"); logger.debug("Loading entitlement agent");
if (EntitlementBootstrap.bootstrapArgs != null) { if (EntitlementBootstrap.bootstrapArgs != null) {
throw new IllegalStateException("plugin data is already set"); throw new IllegalStateException("plugin data is already set");

View file

@ -9,7 +9,6 @@
package org.elasticsearch.entitlement.initialization; package org.elasticsearch.entitlement.initialization;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.core.internal.provider.ProviderLocator; import org.elasticsearch.core.internal.provider.ProviderLocator;
import org.elasticsearch.entitlement.bootstrap.EntitlementBootstrap; import org.elasticsearch.entitlement.bootstrap.EntitlementBootstrap;
import org.elasticsearch.entitlement.bridge.EntitlementChecker; import org.elasticsearch.entitlement.bridge.EntitlementChecker;
@ -96,25 +95,25 @@ public class EntitlementInitialization {
return new PolicyManager(serverPolicy, pluginPolicies, EntitlementBootstrap.bootstrapArgs().pluginResolver()); return new PolicyManager(serverPolicy, pluginPolicies, EntitlementBootstrap.bootstrapArgs().pluginResolver());
} }
private static Map<String, Policy> createPluginPolicies(Collection<Tuple<Path, Boolean>> pluginData) throws IOException { private static Map<String, Policy> createPluginPolicies(Collection<EntitlementBootstrap.PluginData> pluginData) throws IOException {
Map<String, Policy> pluginPolicies = new HashMap<>(pluginData.size()); Map<String, Policy> pluginPolicies = new HashMap<>(pluginData.size());
for (Tuple<Path, Boolean> entry : pluginData) { for (var entry : pluginData) {
Path pluginRoot = entry.v1(); Path pluginRoot = entry.pluginPath();
boolean isModular = entry.v2();
String pluginName = pluginRoot.getFileName().toString(); String pluginName = pluginRoot.getFileName().toString();
final Policy policy = loadPluginPolicy(pluginRoot, isModular, pluginName);
final Policy policy = loadPluginPolicy(pluginRoot, entry.isModular(), pluginName, entry.isExternalPlugin());
pluginPolicies.put(pluginName, policy); pluginPolicies.put(pluginName, policy);
} }
return pluginPolicies; return pluginPolicies;
} }
private static Policy loadPluginPolicy(Path pluginRoot, boolean isModular, String pluginName) throws IOException { private static Policy loadPluginPolicy(Path pluginRoot, boolean isModular, String pluginName, boolean isExternalPlugin)
throws IOException {
Path policyFile = pluginRoot.resolve(POLICY_FILE_NAME); Path policyFile = pluginRoot.resolve(POLICY_FILE_NAME);
final Set<String> moduleNames = getModuleNames(pluginRoot, isModular); final Set<String> moduleNames = getModuleNames(pluginRoot, isModular);
final Policy policy = parsePolicyIfExists(pluginName, policyFile); final Policy policy = parsePolicyIfExists(pluginName, policyFile, isExternalPlugin);
// TODO: should this check actually be part of the parser? // TODO: should this check actually be part of the parser?
for (Scope scope : policy.scopes) { for (Scope scope : policy.scopes) {
@ -125,9 +124,9 @@ public class EntitlementInitialization {
return policy; return policy;
} }
private static Policy parsePolicyIfExists(String pluginName, Path policyFile) throws IOException { private static Policy parsePolicyIfExists(String pluginName, Path policyFile, boolean isExternalPlugin) throws IOException {
if (Files.exists(policyFile)) { if (Files.exists(policyFile)) {
return new PolicyParser(Files.newInputStream(policyFile, StandardOpenOption.READ), pluginName).parsePolicy(); return new PolicyParser(Files.newInputStream(policyFile, StandardOpenOption.READ), pluginName, isExternalPlugin).parsePolicy();
} }
return new Policy(pluginName, List.of()); return new Policy(pluginName, List.of());
} }

View file

@ -33,4 +33,12 @@ public @interface ExternalEntitlement {
* have to match the parameter names of the constructor. * have to match the parameter names of the constructor.
*/ */
String[] parameterNames() default {}; String[] parameterNames() default {};
/**
* This flag indicates if this Entitlement can be used in external plugins,
* or if it can be used only in Elasticsearch modules ("internal" plugins).
* Using an entitlement that is not {@code pluginsAccessible} in an external
* plugin policy will throw in exception while parsing.
*/
boolean esModulesOnly() default true;
} }

View file

@ -26,7 +26,7 @@ public class FileEntitlement implements Entitlement {
private final String path; private final String path;
private final int actions; private final int actions;
@ExternalEntitlement(parameterNames = { "path", "actions" }) @ExternalEntitlement(parameterNames = { "path", "actions" }, esModulesOnly = false)
public FileEntitlement(String path, List<String> actionsList) { public FileEntitlement(String path, List<String> actionsList) {
this.path = path; this.path = path;
int actionsInt = 0; int actionsInt = 0;

View file

@ -39,6 +39,7 @@ public class PolicyParser {
protected final XContentParser policyParser; protected final XContentParser policyParser;
protected final String policyName; protected final String policyName;
private final boolean isExternalPlugin;
static String getEntitlementTypeName(Class<? extends Entitlement> entitlementClass) { static String getEntitlementTypeName(Class<? extends Entitlement> entitlementClass) {
var entitlementClassName = entitlementClass.getSimpleName(); var entitlementClassName = entitlementClass.getSimpleName();
@ -56,9 +57,10 @@ public class PolicyParser {
.collect(Collectors.joining("_")); .collect(Collectors.joining("_"));
} }
public PolicyParser(InputStream inputStream, String policyName) throws IOException { public PolicyParser(InputStream inputStream, String policyName, boolean isExternalPlugin) throws IOException {
this.policyParser = YamlXContent.yamlXContent.createParser(XContentParserConfiguration.EMPTY, Objects.requireNonNull(inputStream)); this.policyParser = YamlXContent.yamlXContent.createParser(XContentParserConfiguration.EMPTY, Objects.requireNonNull(inputStream));
this.policyName = policyName; this.policyName = policyName;
this.isExternalPlugin = isExternalPlugin;
} }
public Policy parsePolicy() { public Policy parsePolicy() {
@ -125,6 +127,10 @@ public class PolicyParser {
throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]"); throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]");
} }
if (entitlementMetadata.esModulesOnly() && isExternalPlugin) {
throw newPolicyParserException("entitlement type [" + entitlementType + "] is allowed only on modules");
}
Class<?>[] parameterTypes = entitlementConstructor.getParameterTypes(); Class<?>[] parameterTypes = entitlementConstructor.getParameterTypes();
String[] parametersNames = entitlementMetadata.parameterNames(); String[] parametersNames = entitlementMetadata.parameterNames();

View file

@ -19,7 +19,7 @@ public class PolicyParserFailureTests extends ESTestCase {
public void testParserSyntaxFailures() { public void testParserSyntaxFailures() {
PolicyParserException ppe = expectThrows( PolicyParserException ppe = expectThrows(
PolicyParserException.class, PolicyParserException.class,
() -> new PolicyParser(new ByteArrayInputStream("[]".getBytes(StandardCharsets.UTF_8)), "test-failure-policy.yaml") () -> new PolicyParser(new ByteArrayInputStream("[]".getBytes(StandardCharsets.UTF_8)), "test-failure-policy.yaml", false)
.parsePolicy() .parsePolicy()
); );
assertEquals("[1:1] policy parsing error for [test-failure-policy.yaml]: expected object <scope name>", ppe.getMessage()); assertEquals("[1:1] policy parsing error for [test-failure-policy.yaml]: expected object <scope name>", ppe.getMessage());
@ -29,7 +29,7 @@ public class PolicyParserFailureTests extends ESTestCase {
PolicyParserException ppe = expectThrows(PolicyParserException.class, () -> new PolicyParser(new ByteArrayInputStream(""" PolicyParserException ppe = expectThrows(PolicyParserException.class, () -> new PolicyParser(new ByteArrayInputStream("""
entitlement-module-name: entitlement-module-name:
- does_not_exist: {} - does_not_exist: {}
""".getBytes(StandardCharsets.UTF_8)), "test-failure-policy.yaml").parsePolicy()); """.getBytes(StandardCharsets.UTF_8)), "test-failure-policy.yaml", false).parsePolicy());
assertEquals( assertEquals(
"[2:5] policy parsing error for [test-failure-policy.yaml] in scope [entitlement-module-name]: " "[2:5] policy parsing error for [test-failure-policy.yaml] in scope [entitlement-module-name]: "
+ "unknown entitlement type [does_not_exist]", + "unknown entitlement type [does_not_exist]",
@ -41,7 +41,7 @@ public class PolicyParserFailureTests extends ESTestCase {
PolicyParserException ppe = expectThrows(PolicyParserException.class, () -> new PolicyParser(new ByteArrayInputStream(""" PolicyParserException ppe = expectThrows(PolicyParserException.class, () -> new PolicyParser(new ByteArrayInputStream("""
entitlement-module-name: entitlement-module-name:
- file: {} - file: {}
""".getBytes(StandardCharsets.UTF_8)), "test-failure-policy.yaml").parsePolicy()); """.getBytes(StandardCharsets.UTF_8)), "test-failure-policy.yaml", false).parsePolicy());
assertEquals( assertEquals(
"[2:12] policy parsing error for [test-failure-policy.yaml] in scope [entitlement-module-name] " "[2:12] policy parsing error for [test-failure-policy.yaml] in scope [entitlement-module-name] "
+ "for entitlement type [file]: missing entitlement parameter [path]", + "for entitlement type [file]: missing entitlement parameter [path]",
@ -52,7 +52,7 @@ public class PolicyParserFailureTests extends ESTestCase {
entitlement-module-name: entitlement-module-name:
- file: - file:
path: test-path path: test-path
""".getBytes(StandardCharsets.UTF_8)), "test-failure-policy.yaml").parsePolicy()); """.getBytes(StandardCharsets.UTF_8)), "test-failure-policy.yaml", false).parsePolicy());
assertEquals( assertEquals(
"[4:1] policy parsing error for [test-failure-policy.yaml] in scope [entitlement-module-name] " "[4:1] policy parsing error for [test-failure-policy.yaml] in scope [entitlement-module-name] "
+ "for entitlement type [file]: missing entitlement parameter [actions]", + "for entitlement type [file]: missing entitlement parameter [actions]",
@ -68,11 +68,22 @@ public class PolicyParserFailureTests extends ESTestCase {
actions: actions:
- read - read
extra: test extra: test
""".getBytes(StandardCharsets.UTF_8)), "test-failure-policy.yaml").parsePolicy()); """.getBytes(StandardCharsets.UTF_8)), "test-failure-policy.yaml", false).parsePolicy());
assertEquals( assertEquals(
"[7:1] policy parsing error for [test-failure-policy.yaml] in scope [entitlement-module-name] " "[7:1] policy parsing error for [test-failure-policy.yaml] in scope [entitlement-module-name] "
+ "for entitlement type [file]: extraneous entitlement parameter(s) {extra=test}", + "for entitlement type [file]: extraneous entitlement parameter(s) {extra=test}",
ppe.getMessage() ppe.getMessage()
); );
} }
public void testEntitlementIsNotForExternalPlugins() {
PolicyParserException ppe = expectThrows(PolicyParserException.class, () -> new PolicyParser(new ByteArrayInputStream("""
entitlement-module-name:
- create_class_loader
""".getBytes(StandardCharsets.UTF_8)), "test-failure-policy.yaml", true).parsePolicy());
assertEquals(
"[2:5] policy parsing error for [test-failure-policy.yaml]: entitlement type [create_class_loader] is allowed only on modules",
ppe.getMessage()
);
}
} }

View file

@ -37,7 +37,17 @@ public class PolicyParserTests extends ESTestCase {
} }
public void testPolicyBuilder() throws IOException { public void testPolicyBuilder() throws IOException {
Policy parsedPolicy = new PolicyParser(PolicyParserTests.class.getResourceAsStream("test-policy.yaml"), "test-policy.yaml") Policy parsedPolicy = new PolicyParser(PolicyParserTests.class.getResourceAsStream("test-policy.yaml"), "test-policy.yaml", false)
.parsePolicy();
Policy builtPolicy = new Policy(
"test-policy.yaml",
List.of(new Scope("entitlement-module-name", List.of(new FileEntitlement("test/path/to/file", List.of("read", "write")))))
);
assertEquals(parsedPolicy, builtPolicy);
}
public void testPolicyBuilderOnExternalPlugin() throws IOException {
Policy parsedPolicy = new PolicyParser(PolicyParserTests.class.getResourceAsStream("test-policy.yaml"), "test-policy.yaml", true)
.parsePolicy(); .parsePolicy();
Policy builtPolicy = new Policy( Policy builtPolicy = new Policy(
"test-policy.yaml", "test-policy.yaml",
@ -50,7 +60,7 @@ public class PolicyParserTests extends ESTestCase {
Policy parsedPolicy = new PolicyParser(new ByteArrayInputStream(""" Policy parsedPolicy = new PolicyParser(new ByteArrayInputStream("""
entitlement-module-name: entitlement-module-name:
- create_class_loader - create_class_loader
""".getBytes(StandardCharsets.UTF_8)), "test-policy.yaml").parsePolicy(); """.getBytes(StandardCharsets.UTF_8)), "test-policy.yaml", false).parsePolicy();
Policy builtPolicy = new Policy( Policy builtPolicy = new Policy(
"test-policy.yaml", "test-policy.yaml",
List.of(new Scope("entitlement-module-name", List.of(new CreateClassLoaderEntitlement()))) List.of(new Scope("entitlement-module-name", List.of(new CreateClassLoaderEntitlement())))

View file

@ -30,7 +30,6 @@ import org.elasticsearch.common.util.concurrent.RunOnce;
import org.elasticsearch.core.AbstractRefCounted; import org.elasticsearch.core.AbstractRefCounted;
import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.IOUtils;
import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.entitlement.bootstrap.EntitlementBootstrap; import org.elasticsearch.entitlement.bootstrap.EntitlementBootstrap;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.IndexVersion;
@ -59,6 +58,7 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import static org.elasticsearch.bootstrap.BootstrapSettings.SECURITY_FILTER_BAD_DEFAULTS_SETTING; import static org.elasticsearch.bootstrap.BootstrapSettings.SECURITY_FILTER_BAD_DEFAULTS_SETTING;
import static org.elasticsearch.nativeaccess.WindowsFunctions.ConsoleCtrlHandler.CTRL_CLOSE_EVENT; import static org.elasticsearch.nativeaccess.WindowsFunctions.ConsoleCtrlHandler.CTRL_CLOSE_EVENT;
@ -218,10 +218,14 @@ class Elasticsearch {
if (Boolean.parseBoolean(System.getProperty("es.entitlements.enabled"))) { if (Boolean.parseBoolean(System.getProperty("es.entitlements.enabled"))) {
LogManager.getLogger(Elasticsearch.class).info("Bootstrapping Entitlements"); LogManager.getLogger(Elasticsearch.class).info("Bootstrapping Entitlements");
List<Tuple<Path, Boolean>> pluginData = pluginsLoader.allBundles() List<EntitlementBootstrap.PluginData> pluginData = Stream.concat(
.stream() pluginsLoader.moduleBundles()
.map(bundle -> Tuple.tuple(bundle.getDir(), bundle.pluginDescriptor().isModular())) .stream()
.toList(); .map(bundle -> new EntitlementBootstrap.PluginData(bundle.getDir(), bundle.pluginDescriptor().isModular(), false)),
pluginsLoader.pluginBundles()
.stream()
.map(bundle -> new EntitlementBootstrap.PluginData(bundle.getDir(), bundle.pluginDescriptor().isModular(), true))
).toList();
EntitlementBootstrap.bootstrap(pluginData, pluginsResolver::resolveClassToPluginName); EntitlementBootstrap.bootstrap(pluginData, pluginsResolver::resolveClassToPluginName);
} else if (RuntimeVersionFeature.isSecurityManagerAvailable()) { } else if (RuntimeVersionFeature.isSecurityManagerAvailable()) {

View file

@ -122,7 +122,8 @@ public class PluginsLoader {
private final List<PluginDescriptor> moduleDescriptors; private final List<PluginDescriptor> moduleDescriptors;
private final List<PluginDescriptor> pluginDescriptors; private final List<PluginDescriptor> pluginDescriptors;
private final Map<String, LoadedPluginLayer> loadedPluginLayers; private final Map<String, LoadedPluginLayer> loadedPluginLayers;
private final Set<PluginBundle> allBundles; private final Set<PluginBundle> moduleBundles;
private final Set<PluginBundle> pluginBundles;
/** /**
* Constructs a new PluginsLoader * Constructs a new PluginsLoader
@ -153,37 +154,36 @@ public class PluginsLoader {
Set<PluginBundle> seenBundles = new LinkedHashSet<>(); Set<PluginBundle> seenBundles = new LinkedHashSet<>();
// load (elasticsearch) module layers // load (elasticsearch) module layers
List<PluginDescriptor> moduleDescriptors; final Set<PluginBundle> modules;
if (modulesDirectory != null) { if (modulesDirectory != null) {
try { try {
Set<PluginBundle> modules = PluginsUtils.getModuleBundles(modulesDirectory); modules = PluginsUtils.getModuleBundles(modulesDirectory);
moduleDescriptors = modules.stream().map(PluginBundle::pluginDescriptor).toList();
seenBundles.addAll(modules); seenBundles.addAll(modules);
} catch (IOException ex) { } catch (IOException ex) {
throw new IllegalStateException("Unable to initialize modules", ex); throw new IllegalStateException("Unable to initialize modules", ex);
} }
} else { } else {
moduleDescriptors = Collections.emptyList(); modules = Collections.emptySet();
} }
// load plugin layers // load plugin layers
List<PluginDescriptor> pluginDescriptors; final Set<PluginBundle> plugins;
if (pluginsDirectory != null) { if (pluginsDirectory != null) {
try { try {
// TODO: remove this leniency, but tests bogusly rely on it // TODO: remove this leniency, but tests bogusly rely on it
if (isAccessibleDirectory(pluginsDirectory, logger)) { if (isAccessibleDirectory(pluginsDirectory, logger)) {
PluginsUtils.checkForFailedPluginRemovals(pluginsDirectory); PluginsUtils.checkForFailedPluginRemovals(pluginsDirectory);
Set<PluginBundle> plugins = PluginsUtils.getPluginBundles(pluginsDirectory); plugins = PluginsUtils.getPluginBundles(pluginsDirectory);
pluginDescriptors = plugins.stream().map(PluginBundle::pluginDescriptor).toList();
seenBundles.addAll(plugins); seenBundles.addAll(plugins);
} else { } else {
pluginDescriptors = Collections.emptyList(); plugins = Collections.emptySet();
} }
} catch (IOException ex) { } catch (IOException ex) {
throw new IllegalStateException("Unable to initialize plugins", ex); throw new IllegalStateException("Unable to initialize plugins", ex);
} }
} else { } else {
pluginDescriptors = Collections.emptyList(); plugins = Collections.emptySet();
} }
Map<String, LoadedPluginLayer> loadedPluginLayers = new LinkedHashMap<>(); Map<String, LoadedPluginLayer> loadedPluginLayers = new LinkedHashMap<>();
@ -197,19 +197,15 @@ public class PluginsLoader {
} }
} }
return new PluginsLoader(moduleDescriptors, pluginDescriptors, loadedPluginLayers, Set.copyOf(seenBundles)); return new PluginsLoader(modules, plugins, loadedPluginLayers);
} }
PluginsLoader( PluginsLoader(Set<PluginBundle> modules, Set<PluginBundle> plugins, Map<String, LoadedPluginLayer> loadedPluginLayers) {
List<PluginDescriptor> moduleDescriptors, this.moduleBundles = modules;
List<PluginDescriptor> pluginDescriptors, this.pluginBundles = plugins;
Map<String, LoadedPluginLayer> loadedPluginLayers, this.moduleDescriptors = modules.stream().map(PluginBundle::pluginDescriptor).toList();
Set<PluginBundle> allBundles this.pluginDescriptors = plugins.stream().map(PluginBundle::pluginDescriptor).toList();
) {
this.moduleDescriptors = moduleDescriptors;
this.pluginDescriptors = pluginDescriptors;
this.loadedPluginLayers = loadedPluginLayers; this.loadedPluginLayers = loadedPluginLayers;
this.allBundles = allBundles;
} }
public List<PluginDescriptor> moduleDescriptors() { public List<PluginDescriptor> moduleDescriptors() {
@ -224,8 +220,12 @@ public class PluginsLoader {
return loadedPluginLayers.values().stream().map(Function.identity()); return loadedPluginLayers.values().stream().map(Function.identity());
} }
public Set<PluginBundle> allBundles() { public Set<PluginBundle> moduleBundles() {
return allBundles; return moduleBundles;
}
public Set<PluginBundle> pluginBundles() {
return pluginBundles;
} }
private static void loadPluginLayer( private static void loadPluginLayer(

View file

@ -45,7 +45,7 @@ public class MockPluginsService extends PluginsService {
super( super(
settings, settings,
environment.configFile(), environment.configFile(),
new PluginsLoader(Collections.emptyList(), Collections.emptyList(), Collections.emptyMap(), Collections.emptySet()) new PluginsLoader(Collections.emptySet(), Collections.emptySet(), Collections.emptyMap())
); );
List<LoadedPlugin> pluginsLoaded = new ArrayList<>(); List<LoadedPlugin> pluginsLoaded = new ArrayList<>();