diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java index 5a14d041c763b..e1733e478b8c2 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java @@ -698,10 +698,13 @@ private void installPlugin(Terminal terminal, boolean isBatch, Path tmpRoot, final PluginInfo info = loadPluginInfo(terminal, tmpRoot, isBatch, env); // read optional security policy (extra permissions), if it exists, confirm or warn the user Path policy = tmpRoot.resolve(PluginInfo.ES_PLUGIN_POLICY); + final Set permissions; if (Files.exists(policy)) { - Set permissions = PluginSecurity.parsePermissions(policy, env.tmpFile()); - PluginSecurity.confirmPolicyExceptions(terminal, permissions, info.hasNativeController(), isBatch); + permissions = PluginSecurity.parsePermissions(policy, env.tmpFile()); + } else { + permissions = Collections.emptySet(); } + PluginSecurity.confirmPolicyExceptions(terminal, permissions, info.hasNativeController(), isBatch); final Path destination = env.pluginsFile().resolve(info.getName()); deleteOnFailure.add(destination); diff --git a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java index d799cb0407f58..96e009b3462f1 100644 --- a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java +++ b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java @@ -1153,6 +1153,59 @@ private Function checksumAndString(final MessageDigest digest, f return bytes -> MessageDigests.toHexString(digest.digest(bytes)) + s; } + // checks the plugin requires a policy confirmation, and does not install when that is rejected by the user + // the plugin is installed after this method completes + private void assertPolicyConfirmation(Tuple env, String pluginZip, String... warnings) throws Exception { + for (int i = 0; i < warnings.length; ++i) { + String warning = warnings[i]; + for (int j = 0; j < i; ++j) { + terminal.addTextInput("y"); // accept warnings we have already tested + } + // default answer, does not install + terminal.addTextInput(""); + UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); + assertEquals("installation aborted by user", e.getMessage()); + + assertThat(terminal.getOutput(), containsString("WARNING: " + warning)); + try (Stream fileStream = Files.list(env.v2().pluginsFile())) { + assertThat(fileStream.collect(Collectors.toList()), empty()); + } + + // explicitly do not install + terminal.reset(); + for (int j = 0; j < i; ++j) { + terminal.addTextInput("y"); // accept warnings we have already tested + } + terminal.addTextInput("n"); + e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); + assertEquals("installation aborted by user", e.getMessage()); + assertThat(terminal.getOutput(), containsString("WARNING: " + warning)); + try (Stream fileStream = Files.list(env.v2().pluginsFile())) { + assertThat(fileStream.collect(Collectors.toList()), empty()); + } + } + + // allow installation + terminal.reset(); + for (int j = 0; j < warnings.length; ++j) { + terminal.addTextInput("y"); + } + installPlugin(pluginZip, env.v1()); + for (String warning : warnings) { + assertThat(terminal.getOutput(), containsString("WARNING: " + warning)); + } + } + + public void testPolicyConfirmation() throws Exception { + Tuple env = createEnv(fs, temp); + Path pluginDir = createPluginDir(temp); + writePluginSecurityPolicy(pluginDir, "setAccessible", "setFactory"); + String pluginZip = createPluginUrl("fake", pluginDir); + + assertPolicyConfirmation(env, pluginZip, "plugin requires additional permissions"); + assertPlugin("fake", pluginDir, env.v2()); + } + public void testMetaPluginPolicyConfirmation() throws Exception { Tuple env = createEnv(fs, temp); Path metaDir = createPluginDir(temp); @@ -1166,32 +1219,60 @@ public void testMetaPluginPolicyConfirmation() throws Exception { writePlugin("fake2", fake2Dir); String pluginZip = createMetaPluginUrl("meta-plugin", metaDir); - // default answer, does not install - terminal.addTextInput(""); - UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); - assertEquals("installation aborted by user", e.getMessage()); - assertThat(terminal.getOutput(), containsString("WARNING: plugin requires additional permissions")); - try (Stream fileStream = Files.list(env.v2().pluginsFile())) { - assertThat(fileStream.collect(Collectors.toList()), empty()); - } + assertPolicyConfirmation(env, pluginZip, "plugin requires additional permissions"); + assertMetaPlugin("meta-plugin", "fake1", metaDir, env.v2()); + assertMetaPlugin("meta-plugin", "fake2", metaDir, env.v2()); + } - // explicitly do not install - terminal.reset(); - terminal.addTextInput("n"); - e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); - assertEquals("installation aborted by user", e.getMessage()); - assertThat(terminal.getOutput(), containsString("WARNING: plugin requires additional permissions")); - try (Stream fileStream = Files.list(env.v2().pluginsFile())) { - assertThat(fileStream.collect(Collectors.toList()), empty()); - } + public void testNativeControllerConfirmation() throws Exception { + Tuple env = createEnv(fs, temp); + Path pluginDir = createPluginDir(temp); + String pluginZip = createPluginUrl("fake", pluginDir, "has.native.controller", "true"); - // allow installation - terminal.reset(); - terminal.addTextInput("y"); - installPlugin(pluginZip, env.v1()); - assertThat(terminal.getOutput(), containsString("WARNING: plugin requires additional permissions")); + assertPolicyConfirmation(env, pluginZip, "plugin forks a native controller"); + assertPlugin("fake", pluginDir, env.v2()); + } + + public void testMetaPluginNativeControllerConfirmation() throws Exception { + Tuple env = createEnv(fs, temp); + Path metaDir = createPluginDir(temp); + Path fake1Dir = metaDir.resolve("fake1"); + Files.createDirectory(fake1Dir); + writePlugin("fake1", fake1Dir, "has.native.controller", "true"); + Path fake2Dir = metaDir.resolve("fake2"); + Files.createDirectory(fake2Dir); + writePlugin("fake2", fake2Dir); + String pluginZip = createMetaPluginUrl("meta-plugin", metaDir); + + assertPolicyConfirmation(env, pluginZip, "plugin forks a native controller"); assertMetaPlugin("meta-plugin", "fake1", metaDir, env.v2()); assertMetaPlugin("meta-plugin", "fake2", metaDir, env.v2()); } + public void testNativeControllerAndPolicyConfirmation() throws Exception { + Tuple env = createEnv(fs, temp); + Path pluginDir = createPluginDir(temp); + writePluginSecurityPolicy(pluginDir, "setAccessible", "setFactory"); + String pluginZip = createPluginUrl("fake", pluginDir, "has.native.controller", "true"); + + assertPolicyConfirmation(env, pluginZip, "plugin requires additional permissions", "plugin forks a native controller"); + assertPlugin("fake", pluginDir, env.v2()); + } + + public void testMetaPluginNativeControllerAndPolicyConfirmation() throws Exception { + Tuple env = createEnv(fs, temp); + Path metaDir = createPluginDir(temp); + Path fake1Dir = metaDir.resolve("fake1"); + Files.createDirectory(fake1Dir); + writePluginSecurityPolicy(fake1Dir, "setAccessible", "setFactory"); + writePlugin("fake1", fake1Dir); + Path fake2Dir = metaDir.resolve("fake2"); + Files.createDirectory(fake2Dir); + writePlugin("fake2", fake2Dir, "has.native.controller", "true"); + String pluginZip = createMetaPluginUrl("meta-plugin", metaDir); + + assertPolicyConfirmation(env, pluginZip, "plugin requires additional permissions", "plugin forks a native controller"); + assertMetaPlugin("meta-plugin", "fake1", metaDir, env.v2()); + assertMetaPlugin("meta-plugin", "fake2", metaDir, env.v2()); + } }