Expose only extension-api to extensions
Unless a plugin declares "Gerrit-ApiType: plugin" in its manifest, assume it is an extension and only make the gerrit-extension-api available to it through the ClassLoader. For non-plugins, do not make any Guice bindings available from the server. This further restricts what an extension can see and do with the system internals. Change-Id: Ia38336c42786afb1419d64c06b0d908ae92a64d1
This commit is contained in:
@@ -20,6 +20,7 @@ import com.google.gerrit.extensions.annotations.PluginData;
|
||||
import com.google.gerrit.extensions.annotations.PluginName;
|
||||
import com.google.gerrit.extensions.registration.RegistrationHandle;
|
||||
import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
|
||||
import com.google.gerrit.extensions.systemstatus.ServerInformation;
|
||||
import com.google.gerrit.lifecycle.LifecycleManager;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
@@ -40,6 +41,10 @@ import java.util.jar.Manifest;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class Plugin {
|
||||
public static enum ApiType {
|
||||
EXTENSION, PLUGIN;
|
||||
}
|
||||
|
||||
static {
|
||||
// Guice logs warnings about multiple injectors being created.
|
||||
// Silence this in case HTTP plugins are used.
|
||||
@@ -47,12 +52,26 @@ public class Plugin {
|
||||
.setLevel(java.util.logging.Level.OFF);
|
||||
}
|
||||
|
||||
static ApiType getApiType(Manifest manifest) throws InvalidPluginException {
|
||||
Attributes main = manifest.getMainAttributes();
|
||||
String v = main.getValue("Gerrit-ApiType");
|
||||
if (Strings.isNullOrEmpty(v)
|
||||
|| ApiType.EXTENSION.name().equalsIgnoreCase(v)) {
|
||||
return ApiType.EXTENSION;
|
||||
} else if (ApiType.PLUGIN.name().equalsIgnoreCase(v)) {
|
||||
return ApiType.PLUGIN;
|
||||
} else {
|
||||
throw new InvalidPluginException("Invalid Gerrit-ApiType: " + v);
|
||||
}
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final File srcJar;
|
||||
private final FileSnapshot snapshot;
|
||||
private final JarFile jarFile;
|
||||
private final Manifest manifest;
|
||||
private final File dataDir;
|
||||
private final ApiType apiType;
|
||||
private final ClassLoader classLoader;
|
||||
private Class<? extends Module> sysModule;
|
||||
private Class<? extends Module> sshModule;
|
||||
@@ -70,6 +89,7 @@ public class Plugin {
|
||||
JarFile jarFile,
|
||||
Manifest manifest,
|
||||
File dataDir,
|
||||
ApiType apiType,
|
||||
ClassLoader classLoader,
|
||||
@Nullable Class<? extends Module> sysModule,
|
||||
@Nullable Class<? extends Module> sshModule,
|
||||
@@ -80,6 +100,7 @@ public class Plugin {
|
||||
this.jarFile = jarFile;
|
||||
this.manifest = manifest;
|
||||
this.dataDir = dataDir;
|
||||
this.apiType = apiType;
|
||||
this.classLoader = classLoader;
|
||||
this.sysModule = sysModule;
|
||||
this.sshModule = sshModule;
|
||||
@@ -94,11 +115,16 @@ public class Plugin {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getVersion() {
|
||||
Attributes main = manifest.getMainAttributes();
|
||||
return main.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
|
||||
}
|
||||
|
||||
public ApiType getApiType() {
|
||||
return apiType;
|
||||
}
|
||||
|
||||
boolean canReload() {
|
||||
Attributes main = manifest.getMainAttributes();
|
||||
String v = main.getValue("Gerrit-ReloadMode");
|
||||
@@ -139,29 +165,33 @@ public class Plugin {
|
||||
}
|
||||
|
||||
if (env.hasSshModule()) {
|
||||
List<Module> modules = Lists.newLinkedList();
|
||||
if (apiType == ApiType.PLUGIN) {
|
||||
modules.add(env.getSshModule());
|
||||
}
|
||||
if (sshModule != null) {
|
||||
sshInjector = sysInjector.createChildInjector(
|
||||
env.getSshModule(),
|
||||
sysInjector.getInstance(sshModule));
|
||||
modules.add(sysInjector.getInstance(sshModule));
|
||||
sshInjector = sysInjector.createChildInjector(modules);
|
||||
manager.add(sshInjector);
|
||||
} else if (auto != null && auto.sshModule != null) {
|
||||
sshInjector = sysInjector.createChildInjector(
|
||||
env.getSshModule(),
|
||||
auto.sshModule);
|
||||
modules.add(auto.sshModule);
|
||||
sshInjector = sysInjector.createChildInjector(modules);
|
||||
manager.add(sshInjector);
|
||||
}
|
||||
}
|
||||
|
||||
if (env.hasHttpModule()) {
|
||||
List<Module> modules = Lists.newLinkedList();
|
||||
if (apiType == ApiType.PLUGIN) {
|
||||
modules.add(env.getHttpModule());
|
||||
}
|
||||
if (httpModule != null) {
|
||||
httpInjector = sysInjector.createChildInjector(
|
||||
env.getHttpModule(),
|
||||
sysInjector.getInstance(httpModule));
|
||||
modules.add(sysInjector.getInstance(httpModule));
|
||||
httpInjector = sysInjector.createChildInjector(modules);
|
||||
manager.add(httpInjector);
|
||||
} else if (auto != null && auto.httpModule != null) {
|
||||
httpInjector = sysInjector.createChildInjector(
|
||||
env.getHttpModule(),
|
||||
auto.httpModule);
|
||||
modules.add(auto.httpModule);
|
||||
httpInjector = sysInjector.createChildInjector(modules);
|
||||
manager.add(httpInjector);
|
||||
}
|
||||
}
|
||||
@@ -169,9 +199,19 @@ public class Plugin {
|
||||
manager.start();
|
||||
}
|
||||
|
||||
private Injector newRootInjector(PluginGuiceEnvironment env) {
|
||||
private Injector newRootInjector(final PluginGuiceEnvironment env) {
|
||||
List<Module> modules = Lists.newArrayListWithCapacity(4);
|
||||
modules.add(env.getSysModule());
|
||||
if (apiType == ApiType.PLUGIN) {
|
||||
modules.add(env.getSysModule());
|
||||
} else {
|
||||
modules.add(new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ServerInformation.class).toInstance(env.getServerInformation());
|
||||
}
|
||||
});
|
||||
}
|
||||
modules.add(new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
|
@@ -19,6 +19,7 @@ import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gerrit.extensions.annotations.PluginName;
|
||||
import com.google.gerrit.extensions.events.LifecycleListener;
|
||||
import com.google.gerrit.extensions.systemstatus.ServerInformation;
|
||||
import com.google.gerrit.server.config.ConfigUtil;
|
||||
@@ -340,7 +341,7 @@ public class PluginLoader implements LifecycleListener {
|
||||
}
|
||||
|
||||
private Plugin loadPlugin(String name, File srcJar, FileSnapshot snapshot)
|
||||
throws IOException, ClassNotFoundException {
|
||||
throws IOException, ClassNotFoundException, InvalidPluginException {
|
||||
File tmp;
|
||||
FileInputStream in = new FileInputStream(srcJar);
|
||||
try {
|
||||
@@ -353,13 +354,20 @@ public class PluginLoader implements LifecycleListener {
|
||||
boolean keep = false;
|
||||
try {
|
||||
Manifest manifest = jarFile.getManifest();
|
||||
Plugin.ApiType type = Plugin.getApiType(manifest);
|
||||
Attributes main = manifest.getMainAttributes();
|
||||
String sysName = main.getValue("Gerrit-Module");
|
||||
String sshName = main.getValue("Gerrit-SshModule");
|
||||
String httpName = main.getValue("Gerrit-HttpModule");
|
||||
|
||||
if (!Strings.isNullOrEmpty(sshName) && type != Plugin.ApiType.PLUGIN) {
|
||||
throw new InvalidPluginException(String.format(
|
||||
"Using Gerrit-SshModule requires Gerrit-ApiType: %s",
|
||||
Plugin.ApiType.PLUGIN));
|
||||
}
|
||||
|
||||
URL[] urls = {tmp.toURI().toURL()};
|
||||
ClassLoader parentLoader = PluginLoader.class.getClassLoader();
|
||||
ClassLoader parentLoader = parentFor(type);
|
||||
ClassLoader pluginLoader = new URLClassLoader(urls, parentLoader);
|
||||
cleanupHandles.put(
|
||||
new CleanupHandle(tmp, jarFile, pluginLoader, cleanupQueue),
|
||||
@@ -372,7 +380,7 @@ public class PluginLoader implements LifecycleListener {
|
||||
return new Plugin(name,
|
||||
srcJar, snapshot,
|
||||
jarFile, manifest,
|
||||
new File(dataDir, name), pluginLoader,
|
||||
new File(dataDir, name), type, pluginLoader,
|
||||
sysModule, sshModule, httpModule);
|
||||
} finally {
|
||||
if (!keep) {
|
||||
@@ -381,6 +389,18 @@ public class PluginLoader implements LifecycleListener {
|
||||
}
|
||||
}
|
||||
|
||||
private static ClassLoader parentFor(Plugin.ApiType type)
|
||||
throws InvalidPluginException {
|
||||
switch (type) {
|
||||
case EXTENSION:
|
||||
return PluginName.class.getClassLoader();
|
||||
case PLUGIN:
|
||||
return PluginLoader.class.getClassLoader();
|
||||
default:
|
||||
throw new InvalidPluginException("Unsupported ApiType " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private static String tempNameFor(String name) {
|
||||
SimpleDateFormat fmt = new SimpleDateFormat("yyMMdd_HHmm");
|
||||
return "plugin_" + name + "_" + fmt.format(new Date()) + "_";
|
||||
|
Reference in New Issue
Block a user