Merge branch 'stable-2.6'
* stable-2.6:
Run plugin startup and shutdown as PluginUser
Allow InternalUser (aka plugins) to see any internal group
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/InternalUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/InternalUser.java
index f13eee9..6f5618b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/InternalUser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/InternalUser.java
@@ -31,6 +31,8 @@
* anything it wants, anytime it wants, given the JVM's own direct access to
* data. Plugins may use this when they need to have a CurrentUser with read
* permission on anything.
+ *
+ * @see PluginUser
*/
public class InternalUser extends CurrentUser {
public interface Factory {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/PluginUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/PluginUser.java
new file mode 100644
index 0000000..490ab07
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/PluginUser.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server;
+
+import com.google.gerrit.server.account.CapabilityControl;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+/** User identity for plugin code that needs an identity. */
+public class PluginUser extends InternalUser {
+ public interface Factory {
+ PluginUser create(String pluginName);
+ }
+
+ private final String pluginName;
+
+ @Inject
+ protected PluginUser(
+ CapabilityControl.Factory capabilityControlFactory,
+ @Assisted String pluginName) {
+ super(capabilityControlFactory);
+ this.pluginName = pluginName;
+ }
+
+ @Override
+ public String getUserName() {
+ return "plugin " + pluginName;
+ }
+
+ @Override
+ public String toString() {
+ return "PluginUser[" + pluginName + "]";
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
index 5c9f012..2a8e7c9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
@@ -21,6 +21,7 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.InternalUser;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -127,6 +128,7 @@
public boolean isVisible() {
AccountGroup accountGroup = GroupDescriptions.toAccountGroup(group);
return (accountGroup != null && accountGroup.isVisibleToAll())
+ || user instanceof InternalUser
|| user.getEffectiveGroups().contains(group.getGroupUUID())
|| isOwner();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 660344a..62c6863 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -33,6 +33,7 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.InternalUser;
import com.google.gerrit.server.MimeUtilFileTypeRegistry;
+import com.google.gerrit.server.PluginUser;
import com.google.gerrit.server.account.AccountByEmailCacheImpl;
import com.google.gerrit.server.account.AccountCacheImpl;
import com.google.gerrit.server.account.AccountControl;
@@ -187,6 +188,7 @@
factory(MergeUtil.Factory.class);
factory(PerformCreateGroup.Factory.class);
factory(PerformRenameGroup.Factory.class);
+ factory(PluginUser.Factory.class);
factory(ProjectNode.Factory.class);
factory(ProjectState.Factory.class);
factory(RebasedPatchSetSender.Factory.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java
index 985272a..8e7192e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java
@@ -21,6 +21,8 @@
import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
import com.google.gerrit.lifecycle.LifecycleManager;
+import com.google.gerrit.server.PluginUser;
+import com.google.gerrit.server.util.RequestContext;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
@@ -83,6 +85,7 @@
private final CacheKey cacheKey;
private final String name;
+ private final PluginUser pluginUser;
private final File srcJar;
private final FileSnapshot snapshot;
private final JarFile jarFile;
@@ -102,6 +105,7 @@
private List<ReloadableRegistrationHandle<?>> reloadableHandles;
public Plugin(String name,
+ PluginUser pluginUser,
File srcJar,
FileSnapshot snapshot,
JarFile jarFile,
@@ -113,6 +117,7 @@
@Nullable Class<? extends Module> sshModule,
@Nullable Class<? extends Module> httpModule) {
this.cacheKey = new CacheKey(name);
+ this.pluginUser = pluginUser;
this.name = name;
this.srcJar = srcJar;
this.snapshot = snapshot;
@@ -131,6 +136,10 @@
return srcJar;
}
+ PluginUser getPluginUser() {
+ return pluginUser;
+ }
+
public CacheKey getCacheKey() {
return cacheKey;
}
@@ -173,6 +182,15 @@
}
void start(PluginGuiceEnvironment env) throws Exception {
+ RequestContext oldContext = env.enter(this);
+ try {
+ startPlugin(env);
+ } finally {
+ env.exit(oldContext);
+ }
+ }
+
+ private void startPlugin(PluginGuiceEnvironment env) throws Exception {
Injector root = newRootInjector(env);
manager = new LifecycleManager();
@@ -235,6 +253,7 @@
modules.add(new AbstractModule() {
@Override
protected void configure() {
+ bind(PluginUser.class).toInstance(pluginUser);
bind(String.class)
.annotatedWith(PluginName.class)
.toInstance(name);
@@ -264,9 +283,14 @@
return Guice.createInjector(modules);
}
- void stop() {
+ void stop(PluginGuiceEnvironment env) {
if (manager != null) {
- manager.stop();
+ RequestContext oldContext = env.enter(this);
+ try {
+ manager.stop();
+ } finally {
+ env.exit(oldContext);
+ }
manager = null;
sysInjector = null;
sshInjector = null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
index 664c278..387ffa4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
@@ -32,6 +32,9 @@
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.server.util.PluginRequestContext;
+import com.google.gerrit.server.util.RequestContext;
+import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.inject.AbstractModule;
import com.google.inject.Binding;
import com.google.inject.Guice;
@@ -65,6 +68,7 @@
public class PluginGuiceEnvironment {
private final Injector sysInjector;
private final ServerInformation srvInfo;
+ private final ThreadLocalRequestContext local;
private final CopyConfigModule copyConfigModule;
private final Set<Key<?>> copyConfigKeys;
private final List<StartPluginListener> onStart;
@@ -90,10 +94,12 @@
@Inject
PluginGuiceEnvironment(
Injector sysInjector,
+ ThreadLocalRequestContext local,
ServerInformation srvInfo,
CopyConfigModule ccm) {
this.sysInjector = sysInjector;
this.srvInfo = srvInfo;
+ this.local = local;
this.copyConfigModule = ccm;
this.copyConfigKeys = Guice.createInjector(ccm).getAllBindings().keySet();
@@ -187,20 +193,33 @@
return httpGen.get();
}
+ RequestContext enter(Plugin plugin) {
+ return local.setContext(new PluginRequestContext(plugin.getPluginUser()));
+ }
+
+ void exit(RequestContext old) {
+ local.setContext(old);
+ }
+
void onStartPlugin(Plugin plugin) {
for (StartPluginListener l : onStart) {
l.onStartPlugin(plugin);
}
- attachItem(sysItems, plugin.getSysInjector(), plugin);
+ RequestContext oldContext = enter(plugin);
+ try {
+ attachItem(sysItems, plugin.getSysInjector(), plugin);
- attachSet(sysSets, plugin.getSysInjector(), plugin);
- attachSet(sshSets, plugin.getSshInjector(), plugin);
- attachSet(httpSets, plugin.getHttpInjector(), plugin);
+ attachSet(sysSets, plugin.getSysInjector(), plugin);
+ attachSet(sshSets, plugin.getSshInjector(), plugin);
+ attachSet(httpSets, plugin.getHttpInjector(), plugin);
- attachMap(sysMaps, plugin.getSysInjector(), plugin);
- attachMap(sshMaps, plugin.getSshInjector(), plugin);
- attachMap(httpMaps, plugin.getHttpInjector(), plugin);
+ attachMap(sysMaps, plugin.getSysInjector(), plugin);
+ attachMap(sshMaps, plugin.getSshInjector(), plugin);
+ attachMap(httpMaps, plugin.getHttpInjector(), plugin);
+ } finally {
+ exit(oldContext);
+ }
}
private void attachItem(Map<TypeLiteral<?>, DynamicItem<?>> items,
@@ -244,15 +263,20 @@
old.put(h.getKey().getTypeLiteral(), h);
}
- reattachMap(old, sysMaps, newPlugin.getSysInjector(), newPlugin);
- reattachMap(old, sshMaps, newPlugin.getSshInjector(), newPlugin);
- reattachMap(old, httpMaps, newPlugin.getHttpInjector(), newPlugin);
+ RequestContext oldContext = enter(newPlugin);
+ try {
+ reattachMap(old, sysMaps, newPlugin.getSysInjector(), newPlugin);
+ reattachMap(old, sshMaps, newPlugin.getSshInjector(), newPlugin);
+ reattachMap(old, httpMaps, newPlugin.getHttpInjector(), newPlugin);
- reattachSet(old, sysSets, newPlugin.getSysInjector(), newPlugin);
- reattachSet(old, sshSets, newPlugin.getSshInjector(), newPlugin);
- reattachSet(old, httpSets, newPlugin.getHttpInjector(), newPlugin);
+ reattachSet(old, sysSets, newPlugin.getSysInjector(), newPlugin);
+ reattachSet(old, sshSets, newPlugin.getSshInjector(), newPlugin);
+ reattachSet(old, httpSets, newPlugin.getHttpInjector(), newPlugin);
- reattachItem(old, sysItems, newPlugin.getSysInjector(), newPlugin);
+ reattachItem(old, sysItems, newPlugin.getSysInjector(), newPlugin);
+ } finally {
+ exit(oldContext);
+ }
}
private void reattachMap(
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 b07f28c..035592c 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
@@ -24,6 +24,7 @@
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.extensions.systemstatus.ServerInformation;
import com.google.gerrit.extensions.webui.JavaScriptPlugin;
+import com.google.gerrit.server.PluginUser;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
@@ -71,6 +72,7 @@
private final File tmpDir;
private final PluginGuiceEnvironment env;
private final ServerInformationImpl srvInfoImpl;
+ private final PluginUser.Factory pluginUserFactory;
private final ConcurrentMap<String, Plugin> running;
private final ConcurrentMap<String, Plugin> disabled;
private final Map<String, FileSnapshot> broken;
@@ -83,6 +85,7 @@
public PluginLoader(SitePaths sitePaths,
PluginGuiceEnvironment pe,
ServerInformationImpl sii,
+ PluginUser.Factory puf,
Provider<PluginCleanerTask> pct,
@GerritServerConfig Config cfg) {
pluginsDir = sitePaths.plugins_dir;
@@ -90,6 +93,7 @@
tmpDir = sitePaths.tmp_dir;
env = pe;
srvInfoImpl = sii;
+ pluginUserFactory = puf;
running = Maps.newConcurrentMap();
disabled = Maps.newConcurrentMap();
broken = Maps.newHashMap();
@@ -193,7 +197,7 @@
synchronized private void unloadPlugin(Plugin plugin) {
String name = plugin.getName();
log.info(String.format("Unloading plugin %s", name));
- plugin.stop();
+ plugin.stop(env);
running.remove(name);
disabled.remove(name);
toCleanup.add(plugin);
@@ -465,7 +469,7 @@
Class<? extends Module> sysModule = load(sysName, pluginLoader);
Class<? extends Module> sshModule = load(sshName, pluginLoader);
Class<? extends Module> httpModule = load(httpName, pluginLoader);
- Plugin plugin = new Plugin(name,
+ Plugin plugin = new Plugin(name, pluginUserFactory.create(name),
srcJar, snapshot,
jarFile, manifest,
new File(dataDir, name), type, pluginLoader,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/PluginRequestContext.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/PluginRequestContext.java
new file mode 100644
index 0000000..a836fd7
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/PluginRequestContext.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.util;
+
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.PluginUser;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+
+/** RequestContext active while plugins load or unload. */
+public class PluginRequestContext implements RequestContext {
+ private final PluginUser user;
+
+ public PluginRequestContext(PluginUser user) {
+ this.user = user;
+ }
+
+ @Override
+ public CurrentUser getCurrentUser() {
+ return user;
+ }
+
+ @Override
+ public Provider<ReviewDb> getReviewDbProvider() {
+ return new Provider<ReviewDb>() {
+ @Override
+ public ReviewDb get() {
+ throw new ProvisionException(
+ "Automatic ReviewDb only available in request scope");
+ }
+ };
+ }
+}