Merge branch 'stable-2.6'
* stable-2.6: Run plugin startup and shutdown as PluginUser Allow InternalUser (aka plugins) to see any internal group
This commit is contained in:
@@ -31,6 +31,8 @@ import java.util.Set;
|
||||
* 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 {
|
||||
|
@@ -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 + "]";
|
||||
}
|
||||
}
|
@@ -21,6 +21,7 @@ import com.google.gerrit.reviewdb.client.Account;
|
||||
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 class GroupControl {
|
||||
public boolean isVisible() {
|
||||
AccountGroup accountGroup = GroupDescriptions.toAccountGroup(group);
|
||||
return (accountGroup != null && accountGroup.isVisibleToAll())
|
||||
|| user instanceof InternalUser
|
||||
|| user.getEffectiveGroups().contains(group.getGroupUUID())
|
||||
|| isOwner();
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ import com.google.gerrit.server.FileTypeRegistry;
|
||||
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 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
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);
|
||||
|
@@ -21,6 +21,8 @@ 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.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 @@ public class Plugin {
|
||||
|
||||
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 @@ public class Plugin {
|
||||
private List<ReloadableRegistrationHandle<?>> reloadableHandles;
|
||||
|
||||
public Plugin(String name,
|
||||
PluginUser pluginUser,
|
||||
File srcJar,
|
||||
FileSnapshot snapshot,
|
||||
JarFile jarFile,
|
||||
@@ -113,6 +117,7 @@ public class Plugin {
|
||||
@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 @@ public class Plugin {
|
||||
return srcJar;
|
||||
}
|
||||
|
||||
PluginUser getPluginUser() {
|
||||
return pluginUser;
|
||||
}
|
||||
|
||||
public CacheKey getCacheKey() {
|
||||
return cacheKey;
|
||||
}
|
||||
@@ -173,6 +182,15 @@ public class Plugin {
|
||||
}
|
||||
|
||||
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 @@ public class Plugin {
|
||||
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 @@ public class Plugin {
|
||||
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;
|
||||
|
@@ -32,6 +32,9 @@ import com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes;
|
||||
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 @@ import javax.inject.Inject;
|
||||
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 @@ public class PluginGuiceEnvironment {
|
||||
@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 @@ public class PluginGuiceEnvironment {
|
||||
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 @@ public class PluginGuiceEnvironment {
|
||||
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(
|
||||
|
@@ -24,6 +24,7 @@ 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.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 @@ public class PluginLoader implements LifecycleListener {
|
||||
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 class PluginLoader implements LifecycleListener {
|
||||
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 @@ public class PluginLoader implements LifecycleListener {
|
||||
tmpDir = sitePaths.tmp_dir;
|
||||
env = pe;
|
||||
srvInfoImpl = sii;
|
||||
pluginUserFactory = puf;
|
||||
running = Maps.newConcurrentMap();
|
||||
disabled = Maps.newConcurrentMap();
|
||||
broken = Maps.newHashMap();
|
||||
@@ -193,7 +197,7 @@ public class PluginLoader implements LifecycleListener {
|
||||
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 @@ public class PluginLoader implements LifecycleListener {
|
||||
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,
|
||||
|
@@ -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");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user