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");
+      }
+    };
+  }
+}