diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 2fc705825b..6c6489e23f 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -2317,6 +2317,12 @@ may force reloading with link:cmd-plugin.html[gerrit plugin reload]. + Default is 1 minute. +[[plugins.allowRemoteAdmin]]plugins.allowRemoteAdmin:: ++ +Enable remote installation, enable and disable of plugins over HTTP +and SSH. If set to true Administrators can install new plugins +remotely, or disable existing plugins. Defaults to false. + [[receive]] === Section receive diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/DisablePlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/DisablePlugin.java index d6dfa3cf71..3a1915a875 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/DisablePlugin.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/DisablePlugin.java @@ -17,6 +17,7 @@ package com.google.gerrit.server.plugins; import com.google.common.collect.ImmutableSet; import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.server.plugins.DisablePlugin.Input; import com.google.gerrit.server.plugins.ListPlugins.PluginInfo; @@ -35,7 +36,12 @@ class DisablePlugin implements RestModifyView { } @Override - public PluginInfo apply(PluginResource resource, Input input) { + public PluginInfo apply(PluginResource resource, Input input) + throws MethodNotAllowedException { + if (!loader.isRemoteAdminEnabled()) { + throw new MethodNotAllowedException( + "remote plugin administration is disabled"); + } String name = resource.getName(); loader.disablePlugins(ImmutableSet.of(name)); return new PluginInfo(loader.get(name)); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/EnablePlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/EnablePlugin.java index ebe6d1a984..f7745ccf40 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/EnablePlugin.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/EnablePlugin.java @@ -17,6 +17,7 @@ package com.google.gerrit.server.plugins; import com.google.common.collect.ImmutableSet; import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.server.plugins.EnablePlugin.Input; @@ -40,7 +41,11 @@ class EnablePlugin implements RestModifyView { @Override public PluginInfo apply(PluginResource resource, Input input) - throws ResourceConflictException { + throws ResourceConflictException, MethodNotAllowedException { + if (!loader.isRemoteAdminEnabled()) { + throw new MethodNotAllowedException( + "remote plugin administration is disabled"); + } String name = resource.getName(); try { loader.enablePlugins(ImmutableSet.of(name)); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java index f9bdb48024..33e0d124dc 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java @@ -18,6 +18,7 @@ import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.DefaultInput; +import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.RawInput; import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestModifyView; @@ -53,8 +54,11 @@ class InstallPlugin implements RestModifyView { } @Override - public Response apply(TopLevelResource resource, - Input input) throws BadRequestException, IOException { + public Response apply(TopLevelResource resource, Input input) + throws BadRequestException, MethodNotAllowedException, IOException { + if (!loader.isRemoteAdminEnabled()) { + throw new MethodNotAllowedException("remote installation is disabled"); + } try { InputStream in; if (input.raw != null) { @@ -102,8 +106,8 @@ class InstallPlugin implements RestModifyView { } @Override - public Response apply(PluginResource resource, - Input input) throws BadRequestException, IOException { + public Response apply(PluginResource resource, Input input) + throws BadRequestException, MethodNotAllowedException, IOException { return new InstallPlugin(loader, resource.getName(), false) .apply(TopLevelResource.INSTANCE, input); } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java index ee27de3558..732da4b2aa 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java @@ -90,6 +90,7 @@ public class PluginLoader implements LifecycleListener { private final Provider cleaner; private final PluginScannerThread scanner; private final Provider urlProvider; + private final boolean remoteAdmin; @Inject public PluginLoader(SitePaths sitePaths, @@ -113,6 +114,9 @@ public class PluginLoader implements LifecycleListener { cleaner = pct; urlProvider = provider; + remoteAdmin = + cfg.getBoolean("plugins", null, "allowRemoteAdmin", false); + long checkFrequency = ConfigUtil.getTimeUnit(cfg, "plugins", null, "checkFrequency", TimeUnit.MINUTES.toMillis(1), TimeUnit.MILLISECONDS); @@ -123,6 +127,10 @@ public class PluginLoader implements LifecycleListener { } } + public boolean isRemoteAdminEnabled() { + return remoteAdmin; + } + public Plugin get(String name) { Plugin p = running.get(name); if (p != null) { @@ -143,6 +151,8 @@ public class PluginLoader implements LifecycleListener { public void installPluginFromStream(String originalName, InputStream in) throws IOException, PluginInstallException { + checkRemoteInstall(); + String fileName = originalName; if (!(fileName.endsWith(".jar") || fileName.endsWith(".js"))) { fileName += ".jar"; @@ -227,6 +237,12 @@ public class PluginLoader implements LifecycleListener { } public void disablePlugins(Set names) { + if (!isRemoteAdminEnabled()) { + log.warn("Remote plugin administration is disabled," + + " ignoring disablePlugins(" + names + ")"); + return; + } + synchronized (this) { for (String name : names) { Plugin active = running.get(name); @@ -255,6 +271,12 @@ public class PluginLoader implements LifecycleListener { } public void enablePlugins(Set names) throws PluginInstallException { + if (!isRemoteAdminEnabled()) { + log.warn("Remote plugin administration is disabled," + + " ignoring enablePlugins(" + names + ")"); + return; + } + synchronized (this) { for (String name : names) { Plugin off = disabled.get(name); @@ -748,4 +770,10 @@ public class PluginLoader implements LifecycleListener { String fullExt = "." + ext; return fileName.endsWith(fullExt) || fileName.endsWith(fullExt + ".disabled"); } + + private void checkRemoteInstall() throws PluginInstallException { + if (!isRemoteAdminEnabled()) { + throw new PluginInstallException("remote installation is disabled"); + } + } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginsCollection.java index 6d30b4223d..076bfdd1ec 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginsCollection.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginsCollection.java @@ -17,6 +17,7 @@ package com.google.gerrit.server.plugins; import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.IdString; +import com.google.gerrit.extensions.restapi.MethodNotAllowedException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestCollection; import com.google.gerrit.extensions.restapi.RestView; @@ -58,7 +59,10 @@ public class PluginsCollection implements @SuppressWarnings("unchecked") @Override public InstallPlugin create(TopLevelResource parent, IdString id) - throws ResourceNotFoundException { + throws ResourceNotFoundException, MethodNotAllowedException { + if (!loader.isRemoteAdminEnabled()) { + throw new MethodNotAllowedException("remote installation is disabled"); + } return new InstallPlugin(loader, id.get(), true /* created */); } diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginEnableCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginEnableCommand.java index b2afde14db..1d3ee9fbd4 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginEnableCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginEnableCommand.java @@ -41,6 +41,9 @@ final class PluginEnableCommand extends SshCommand { @Override protected void run() throws UnloggedFailure { + if (!loader.isRemoteAdminEnabled()) { + throw die("remote plugin administration is disabled"); + } if (names != null && !names.isEmpty()) { try { loader.enablePlugins(Sets.newHashSet(names)); diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java index 71f15172ee..49120f7f4b 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java @@ -56,6 +56,9 @@ final class PluginInstallCommand extends SshCommand { @Override protected void run() throws UnloggedFailure { + if (!loader.isRemoteAdminEnabled()) { + throw die("remote installation is disabled"); + } if (Strings.isNullOrEmpty(source)) { throw die("Argument \"-|URL\" is required"); } diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java index 757348f1b6..8e87b0f4de 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java @@ -39,7 +39,10 @@ final class PluginRemoveCommand extends SshCommand { private PluginLoader loader; @Override - protected void run() { + protected void run() throws UnloggedFailure { + if (!loader.isRemoteAdminEnabled()) { + throw die("remote plugin administration is disabled"); + } if (names != null && !names.isEmpty()) { loader.disablePlugins(Sets.newHashSet(names)); }