提交 89cbfc46 编写于 作者: J Jesse Glick

Merge branch 'SECURITY-144' into SECURITY-144-rc

Conflicts:
	core/src/main/java/hudson/EnvVars.java
	core/src/main/java/hudson/FilePath.java
	core/src/main/java/hudson/cli/ClientAuthenticationCache.java
	core/src/main/java/hudson/markup/MyspacePolicy.java
	core/src/main/java/hudson/model/Computer.java
	core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java
	core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java
	core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java
	core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java
	core/src/main/java/hudson/slaves/Channels.java
	core/src/main/java/hudson/slaves/SlaveComputer.java
	core/src/main/java/hudson/tasks/Fingerprinter.java
	core/src/main/java/hudson/tasks/Shell.java
	core/src/main/java/hudson/tasks/junit/JUnitParser.java
	core/src/main/java/hudson/tasks/test/DefaultTestResultParserImpl.java
	core/src/main/java/hudson/util/ProcessTree.java
	core/src/main/java/hudson/util/RemotingDiagnostics.java
	core/src/main/java/jenkins/model/Jenkins.java
	core/src/test/java/hudson/LauncherTest.java
	pom.xml
	test/src/test/java/hudson/tasks/junit/CaseResultTest.java
......@@ -23,12 +23,12 @@
*/
package hudson;
import hudson.remoting.Callable;
import hudson.remoting.VirtualChannel;
import hudson.util.CaseInsensitiveComparator;
import hudson.util.CyclicGraphDetector;
import hudson.util.CyclicGraphDetector.CycleDetectedException;
import hudson.util.VariableResolver;
import jenkins.security.MasterToSlaveCallable;
import java.io.File;
import java.io.IOException;
......@@ -404,7 +404,7 @@ public class EnvVars extends TreeMap<String,String> {
return channel.call(new GetEnvVars());
}
private static final class GetEnvVars implements Callable<EnvVars,RuntimeException> {
private static final class GetEnvVars extends MasterToSlaveCallable<EnvVars,RuntimeException> {
public EnvVars call() {
return new EnvVars(EnvVars.masterEnvVars);
}
......
......@@ -38,6 +38,7 @@ import hudson.remoting.VirtualChannel;
import hudson.util.StreamCopyThread;
import hudson.util.ArgumentListBuilder;
import hudson.util.ProcessTree;
import jenkins.security.MasterToSlaveCallable;
import org.apache.commons.io.input.NullInputStream;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
......@@ -940,7 +941,7 @@ public abstract class Launcher {
getChannel().call(new KillTask(modelEnvVars));
}
private static final class KillTask implements Callable<Void,RuntimeException> {
private static final class KillTask extends MasterToSlaveCallable<Void,RuntimeException> {
private final Map<String, String> modelEnvVars;
public KillTask(Map<String, String> modelEnvVars) {
......@@ -1097,7 +1098,7 @@ public abstract class Launcher {
IOTriplet getIOtriplet();
}
private static class RemoteLaunchCallable implements Callable<RemoteProcess,IOException> {
private static class RemoteLaunchCallable extends MasterToSlaveCallable<RemoteProcess,IOException> {
private final List<String> cmd;
private final boolean[] masks;
private final String[] env;
......@@ -1169,7 +1170,7 @@ public abstract class Launcher {
private static final long serialVersionUID = 1L;
}
private static class RemoteChannelLaunchCallable implements Callable<OutputStream,IOException> {
private static class RemoteChannelLaunchCallable extends MasterToSlaveCallable<OutputStream,IOException> {
private final String[] cmd;
private final Pipe out;
private final String workDir;
......
......@@ -36,6 +36,7 @@ import hudson.remoting.Channel;
import hudson.remoting.ChannelProperty;
import hudson.security.CliAuthenticator;
import hudson.security.SecurityRealm;
import jenkins.security.MasterToSlaveCallable;
import org.acegisecurity.Authentication;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.context.SecurityContext;
......@@ -409,7 +410,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
return checkChannel().call(new GetSystemProperty(name));
}
private static final class GetSystemProperty implements Callable<String, IOException> {
private static final class GetSystemProperty extends MasterToSlaveCallable<String, IOException> {
private final String name;
private GetSystemProperty(String name) {
......@@ -438,7 +439,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
}
}
private static final class GetCharset implements Callable<String, IOException> {
private static final class GetCharset extends MasterToSlaveCallable<String, IOException> {
public String call() throws IOException {
return Charset.defaultCharset().name();
}
......@@ -453,7 +454,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
return checkChannel().call(new GetEnvironmentVariable(name));
}
private static final class GetEnvironmentVariable implements Callable<String, IOException> {
private static final class GetEnvironmentVariable extends MasterToSlaveCallable<String, IOException> {
private final String name;
private GetEnvironmentVariable(String name) {
......
package hudson.cli;
import hudson.FilePath;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.util.Secret;
import jenkins.model.Jenkins;
import jenkins.security.MasterToSlaveCallable;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
......@@ -39,7 +39,7 @@ public class ClientAuthenticationCache implements Serializable {
private final Properties props = new Properties();
public ClientAuthenticationCache(Channel channel) throws IOException, InterruptedException {
store = (channel==null ? FilePath.localChannel : channel).call(new Callable<FilePath, IOException>() {
store = (channel==null ? FilePath.localChannel : channel).call(new MasterToSlaveCallable<FilePath, IOException>() {
public FilePath call() throws IOException {
File home = new File(System.getProperty("user.home"));
File hudsonHome = new File(home, ".hudson");
......
......@@ -28,6 +28,7 @@ import jenkins.model.Jenkins;
import hudson.model.Job;
import hudson.model.Run;
import hudson.remoting.Callable;
import jenkins.security.MasterToSlaveCallable;
import org.kohsuke.args4j.CmdLineException;
import java.io.IOException;
......@@ -84,7 +85,7 @@ public abstract class CommandDuringBuild extends CLICommand {
/**
* Gets the environment variables that points to the build being executed.
*/
private static final class GetCharacteristicEnvironmentVariables implements Callable<String[],IOException> {
private static final class GetCharacteristicEnvironmentVariables extends MasterToSlaveCallable<String[],IOException> {
public String[] call() throws IOException {
return new String[] {
System.getenv("JOB_NAME"),
......
......@@ -31,10 +31,7 @@ import hudson.model.AbstractProject;
import hudson.model.Run;
import hudson.model.Executor;
import hudson.model.Node;
import hudson.model.EnvironmentSpecific;
import hudson.model.Item;
import hudson.remoting.Callable;
import hudson.slaves.NodeSpecific;
import hudson.util.EditDistance;
import hudson.util.StreamTaskListener;
import hudson.tools.ToolDescriptor;
......@@ -44,6 +41,7 @@ import java.util.List;
import java.util.ArrayList;
import java.io.IOException;
import jenkins.security.MasterToSlaveCallable;
import org.kohsuke.args4j.Argument;
/**
......@@ -131,7 +129,7 @@ public class InstallToolCommand extends CLICommand {
return 0;
}
private static final class BuildIDs implements Callable<BuildIDs, IOException> {
private static final class BuildIDs extends MasterToSlaveCallable<BuildIDs, IOException> {
String job,number,id;
public BuildIDs call() throws IOException {
......
package hudson.cli.util;
import hudson.AbortException;
import hudson.remoting.Callable;
import jenkins.security.MasterToSlaveCallable;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.IOException;
......@@ -9,15 +11,12 @@ import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
/**
*
* @author vjuranek
* Reads a file (either a path or URL) over a channel.
*
* @author vjuranek
*/
public class ScriptLoader implements Callable<String,IOException> {
public class ScriptLoader extends MasterToSlaveCallable<String,IOException> {
private final String script;
......@@ -43,5 +42,4 @@ public class ScriptLoader implements Callable<String,IOException> {
s.close();
}
}
}
......@@ -40,6 +40,7 @@ import hudson.slaves.ComputerListener;
import hudson.util.CopyOnWriteList;
import hudson.util.RingBufferLogHandler;
import hudson.util.XStream2;
import jenkins.security.MasterToSlaveCallable;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.*;
import org.kohsuke.stapler.interceptor.RequirePOST;
......@@ -207,7 +208,7 @@ public class LogRecorder extends AbstractModelObject implements Saveable {
}
private static final class SetLevel implements Callable<Void,Error> {
private static final class SetLevel extends MasterToSlaveCallable<Void,Error> {
/** known loggers (kept per slave), to avoid GC */
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private static final Set<Logger> loggers = new HashSet<Logger>();
private final String name;
......
......@@ -34,7 +34,6 @@ import hudson.cli.declarative.CLIMethod;
import hudson.cli.declarative.CLIResolver;
import hudson.model.listeners.ItemListener;
import hudson.model.listeners.SaveableListener;
import hudson.remoting.Callable;
import hudson.security.AccessControlled;
import hudson.security.Permission;
import hudson.security.ACL;
......@@ -43,6 +42,7 @@ import hudson.util.AlternativeUiTextProvider.Message;
import hudson.util.AtomicFileWriter;
import hudson.util.IOUtils;
import jenkins.model.Jenkins;
import jenkins.security.NotReallyRoleSensitiveCallable;
import org.acegisecurity.Authentication;
import org.apache.tools.ant.taskdefs.Copy;
import org.apache.tools.ant.types.FileSet;
......@@ -226,7 +226,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
// the test to see if the project already exists or not needs to be done in escalated privilege
// to avoid overwriting
ACL.impersonate(ACL.SYSTEM,new Callable<Void,IOException>() {
ACL.impersonate(ACL.SYSTEM,new NotReallyRoleSensitiveCallable<Void,IOException>() {
final Authentication user = Jenkins.getAuthentication();
@Override
public Void call() throws IOException {
......@@ -657,7 +657,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
throw new IOException("Expecting "+this.getClass()+" but got "+o.getClass()+" instead");
}
Items.whileUpdatingByXml(new Callable<Void,IOException>() {
Items.whileUpdatingByXml(new NotReallyRoleSensitiveCallable<Void,IOException>() {
@Override public Void call() throws IOException {
onLoad(getParent(), getRootDir().getName());
return null;
......@@ -689,7 +689,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
// try to reflect the changes by reloading
getConfigFile().unmarshal(this);
Items.whileUpdatingByXml(new Callable<Void, IOException>() {
Items.whileUpdatingByXml(new NotReallyRoleSensitiveCallable<Void, IOException>() {
@Override
public Void call() throws IOException {
onLoad(getParent(), getRootDir().getName());
......
......@@ -38,7 +38,6 @@ import hudson.model.queue.WorkUnit;
import hudson.node_monitors.NodeMonitor;
import hudson.remoting.Channel;
import hudson.remoting.VirtualChannel;
import hudson.remoting.Callable;
import hudson.security.ACL;
import hudson.security.AccessControlled;
import hudson.security.Permission;
......@@ -61,6 +60,7 @@ import hudson.util.Futures;
import hudson.util.NamingThreadFactory;
import jenkins.model.Jenkins;
import jenkins.util.ContextResettingExecutorService;
import jenkins.security.MasterToSlaveCallable;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.stapler.StaplerRequest;
......@@ -1042,7 +1042,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
oneOffExecutors.remove(e);
}
private static class ListPossibleNames implements Callable<List<String>,IOException> {
private static class ListPossibleNames extends MasterToSlaveCallable<List<String>,IOException> {
public List<String> call() throws IOException {
List<String> names = new ArrayList<String>();
......@@ -1072,7 +1072,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
private static final long serialVersionUID = 1L;
}
private static class GetFallbackName implements Callable<String,IOException> {
private static class GetFallbackName extends MasterToSlaveCallable<String,IOException> {
public String call() throws IOException {
return System.getProperty("host.name");
}
......@@ -1150,7 +1150,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
w.close();
}
private static final class DumpExportTableTask implements Callable<String,IOException> {
private static final class DumpExportTableTask extends MasterToSlaveCallable<String,IOException> {
public String call() throws IOException {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
......
......@@ -25,7 +25,6 @@ package hudson.model;
import hudson.FilePath;
import hudson.Util;
import hudson.remoting.Callable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
......@@ -43,6 +42,7 @@ import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import jenkins.model.Jenkins;
import jenkins.security.MasterToSlaveCallable;
import jenkins.util.VirtualFile;
import org.apache.commons.io.IOUtils;
import org.apache.tools.zip.ZipEntry;
......@@ -461,7 +461,7 @@ public final class DirectoryBrowserSupport implements HttpResponse {
}
}
private static final class BuildChildPaths implements Callable<List<List<Path>>,IOException> {
private static final class BuildChildPaths extends MasterToSlaveCallable<List<List<Path>>,IOException> {
private final VirtualFile cur;
private final Locale locale;
BuildChildPaths(VirtualFile cur, Locale locale) {
......
......@@ -26,7 +26,6 @@ package hudson.model;
import hudson.Util;
import hudson.XmlFile;
import hudson.model.listeners.ItemListener;
import hudson.remoting.Callable;
import hudson.security.AccessControlled;
import hudson.util.CopyOnWriteMap;
import hudson.util.Function1;
......@@ -44,6 +43,7 @@ import java.io.InputStream;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.security.NotReallyRoleSensitiveCallable;
/**
* Defines a bunch of static methods to be used as a "mix-in" for {@link ItemGroup}
......@@ -222,7 +222,7 @@ public abstract class ItemGroupMixIn {
// reload from the new config
final File rootDir = result.getRootDir();
result = Items.whileUpdatingByXml(new Callable<T,IOException>() {
result = Items.whileUpdatingByXml(new NotReallyRoleSensitiveCallable<T,IOException>() {
@Override public T call() throws IOException {
return (T) Items.load(parent, rootDir);
}
......@@ -253,7 +253,7 @@ public abstract class ItemGroupMixIn {
IOUtils.copy(xml,configXml);
// load it
TopLevelItem result = Items.whileUpdatingByXml(new Callable<TopLevelItem,IOException>() {
TopLevelItem result = Items.whileUpdatingByXml(new NotReallyRoleSensitiveCallable<TopLevelItem,IOException>() {
@Override public TopLevelItem call() throws IOException {
return (TopLevelItem) Items.load(parent, dir);
}
......
......@@ -59,6 +59,7 @@ import javax.servlet.ServletException;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import jenkins.model.Jenkins;
import jenkins.security.MasterToSlaveCallable;
import jenkins.slaves.WorkspaceLocator;
import org.apache.commons.io.IOUtils;
......@@ -455,7 +456,7 @@ public abstract class Slave extends Node implements Serializable {
* <li>When it's read on this side as a return value, it morphs itself into {@link ClockDifference}.
* </ol>
*/
private static final class GetClockDifference1 implements Callable<ClockDifference,IOException> {
private static final class GetClockDifference1 extends MasterToSlaveCallable<ClockDifference,IOException> {
public ClockDifference call() {
// this method must be being invoked locally, which means the clock is in sync
return new ClockDifference(0);
......@@ -468,7 +469,7 @@ public abstract class Slave extends Node implements Serializable {
private static final long serialVersionUID = 1L;
}
private static final class GetClockDifference2 implements Callable<GetClockDifference3,IOException> {
private static final class GetClockDifference2 extends MasterToSlaveCallable<GetClockDifference3,IOException> {
/**
* Capture the time on the master when this object is sent to remote, which is when
* {@link GetClockDifference1#writeReplace()} is run.
......
......@@ -26,6 +26,7 @@ package hudson.node_monitors;
import hudson.model.Computer;
import hudson.remoting.Callable;
import hudson.Extension;
import jenkins.security.MasterToSlaveCallable;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
......@@ -56,7 +57,7 @@ public class ArchitectureMonitor extends NodeMonitor {
/**
* Obtains the string that represents the architecture.
*/
private static class GetArchTask implements Callable<String,IOException> {
private static class GetArchTask extends MasterToSlaveCallable<String,IOException> {
public String call() {
String os = System.getProperty("os.name");
String arch = System.getProperty("os.arch");
......
......@@ -23,8 +23,8 @@
*/
package hudson.node_monitors;
import hudson.FilePath.FileCallable;
import hudson.Functions;
import jenkins.MasterToSlaveFileCallable;
import hudson.remoting.VirtualChannel;
import hudson.Util;
import hudson.slaves.OfflineCause;
......@@ -154,7 +154,7 @@ public abstract class DiskSpaceMonitorDescriptor extends AbstractAsyncNodeMonito
private static final long serialVersionUID = 2L;
}
protected static final class GetUsableSpace implements FileCallable<DiskSpace> {
protected static final class GetUsableSpace extends MasterToSlaveFileCallable<DiskSpace> {
public GetUsableSpace() {}
public DiskSpace invoke(File f, VirtualChannel channel) throws IOException {
long s = f.getUsableSpace();
......
......@@ -28,6 +28,7 @@ import hudson.Extension;
import hudson.slaves.OfflineCause;
import hudson.model.Computer;
import hudson.remoting.Callable;
import jenkins.security.MasterToSlaveCallable;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
......@@ -85,7 +86,7 @@ public class ResponseTimeMonitor extends NodeMonitor {
}
};
private static final class Step1 implements Callable<Data,IOException> {
private static final class Step1 extends MasterToSlaveCallable<Data,IOException> {
private Data cur;
private Step1(Data cur) {
......@@ -104,7 +105,7 @@ public class ResponseTimeMonitor extends NodeMonitor {
private static final long serialVersionUID = 1L;
}
private static final class Step2 implements Callable<Step3,IOException> {
private static final class Step2 extends MasterToSlaveCallable<Step3,IOException> {
private final Data cur;
private final long start = System.currentTimeMillis();
......
......@@ -28,7 +28,7 @@ import hudson.Extension;
import hudson.Functions;
import hudson.model.Computer;
import jenkins.model.Jenkins;
import hudson.remoting.Callable;
import jenkins.security.MasterToSlaveCallable;
import net.sf.json.JSONObject;
import org.jvnet.hudson.MemoryMonitor;
import org.jvnet.hudson.MemoryUsage;
......@@ -100,7 +100,7 @@ public class SwapSpaceMonitor extends NodeMonitor {
/**
* Obtains the string that represents the architecture.
*/
private static class MonitorTask implements Callable<MemoryUsage,IOException> {
private static class MonitorTask extends MasterToSlaveCallable<MemoryUsage,IOException> {
public MemoryUsage call() throws IOException {
MemoryMonitor mm;
try {
......
......@@ -25,7 +25,7 @@ package hudson.node_monitors;
import hudson.Extension;
import hudson.FilePath;
import hudson.FilePath.FileCallable;
import jenkins.MasterToSlaveFileCallable;
import hudson.model.Computer;
import hudson.model.Node;
import hudson.remoting.Callable;
......@@ -83,7 +83,7 @@ public class TemporarySpaceMonitor extends AbstractDiskSpaceMonitor {
return DESCRIPTOR;
}
protected static final class GetTempSpace implements FileCallable<DiskSpace> {
protected static final class GetTempSpace extends MasterToSlaveFileCallable<DiskSpace> {
public DiskSpace invoke(File f, VirtualChannel channel) throws IOException {
// if the disk is really filled up we can't even create a single file,
// so calling File.createTempFile and figuring out the directory won't reliably work.
......
......@@ -38,6 +38,7 @@ import hudson.util.HudsonIsRestarting;
import hudson.util.StreamTaskListener;
import static hudson.util.jna.GNUCLibrary.*;
import jenkins.security.MasterToSlaveCallable;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.jvnet.libpam.impl.CLibrary.passwd;
import org.jvnet.solaris.libzfs.ACLBuilder;
......@@ -167,7 +168,7 @@ public class ZFSInstaller extends AdministrativeMonitor implements Serializable
// this is the actual creation of the file system.
// return true indicating a success
return SU.execute(listener, rootUsername, rootPassword, new Callable<String,IOException>() {
return SU.execute(listener, rootUsername, rootPassword, new MasterToSlaveCallable<String,IOException>() {
private static final long serialVersionUID = 7731167233498214301L;
public String call() throws IOException {
......
......@@ -23,13 +23,13 @@
*/
package hudson.os.solaris;
import jenkins.MasterToSlaveFileCallable;
import hudson.FileSystemProvisioner;
import hudson.FilePath;
import hudson.WorkspaceSnapshot;
import hudson.FileSystemProvisionerDescriptor;
import hudson.Extension;
import hudson.remoting.VirtualChannel;
import hudson.FilePath.FileCallable;
import hudson.model.AbstractBuild;
import hudson.model.TaskListener;
import hudson.model.AbstractProject;
......@@ -52,7 +52,7 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl
private final String rootDataset;
public ZFSProvisioner(Node node) throws IOException, InterruptedException {
rootDataset = node.getRootPath().act(new FileCallable<String>() {
rootDataset = node.getRootPath().act(new MasterToSlaveFileCallable<String>() {
private static final long serialVersionUID = -2142349338699797436L;
public String invoke(File f, VirtualChannel channel) throws IOException {
......@@ -68,7 +68,7 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl
public void prepareWorkspace(AbstractBuild<?,?> build, FilePath ws, final TaskListener listener) throws IOException, InterruptedException {
final String name = build.getProject().getFullName();
ws.act(new FileCallable<Void>() {
ws.act(new MasterToSlaveFileCallable<Void>() {
private static final long serialVersionUID = 2129531727963121198L;
public Void invoke(File f, VirtualChannel channel) throws IOException {
......@@ -87,7 +87,7 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl
}
public void discardWorkspace(AbstractProject<?, ?> project, FilePath ws) throws IOException, InterruptedException {
ws.act(new FileCallable<Void>() {
ws.act(new MasterToSlaveFileCallable<Void>() {
private static final long serialVersionUID = 1916618107019257530L;
public Void invoke(File f, VirtualChannel channel) throws IOException {
......
......@@ -3,13 +3,13 @@ package hudson.security;
import groovy.lang.Binding;
import hudson.FilePath;
import hudson.cli.CLICommand;
import hudson.remoting.Callable;
import hudson.util.spring.BeanBuilder;
import java.io.Console;
import java.io.IOException;
import jenkins.model.Jenkins;
import jenkins.security.ImpersonatingUserDetailsService;
import jenkins.security.SecurityListener;
import jenkins.security.MasterToSlaveCallable;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.AuthenticationManager;
......@@ -152,7 +152,7 @@ public abstract class AbstractPasswordBasedSecurityRealm extends SecurityRealm i
/**
* Asks for the password.
*/
private static class InteractivelyAskForPassword implements Callable<String,IOException> {
private static class InteractivelyAskForPassword extends MasterToSlaveCallable<String,IOException> {
public String call() throws IOException {
Console console = System.console();
if (console == null) return null; // no terminal
......
......@@ -28,9 +28,9 @@ import hudson.FilePath;
import hudson.model.Computer;
import hudson.model.Slave;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.PingThread;
import jenkins.security.MasterToSlaveCallable;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
......@@ -89,7 +89,7 @@ public class ChannelPinger extends ComputerListener {
setUpPingForChannel(channel, pingInterval);
}
private static class SetUpRemotePing implements Callable<Void, IOException> {
private static class SetUpRemotePing extends MasterToSlaveCallable<Void, IOException> {
private static final long serialVersionUID = -2702219700841759872L;
private int pingInterval;
public SetUpRemotePing(int pingInterval) {
......
......@@ -29,14 +29,18 @@ import hudson.FilePath;
import hudson.model.Computer;
import hudson.model.TaskListener;
import hudson.remoting.Channel;
import hudson.remoting.ChannelBuilder;
import hudson.remoting.CommandTransport;
import hudson.remoting.Launcher;
import hudson.remoting.SocketChannelStream;
import hudson.util.ClasspathBuilder;
import hudson.util.JVMBuilder;
import hudson.util.StreamCopyThread;
import jenkins.security.ChannelConfigurator;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
......@@ -67,66 +71,84 @@ public class Channels {
* we kill the process.
*/
public static Channel forProcess(String name, ExecutorService execService, InputStream in, OutputStream out, OutputStream header, final Proc proc) throws IOException {
return new Channel(name, execService, in, out, header) {
/**
* Kill the process when the channel is severed.
*/
ChannelBuilder cb = new ChannelBuilder(name,execService) {
@Override
public synchronized void terminate(IOException e) {
super.terminate(e);
try {
proc.kill();
} catch (IOException x) {
// we are already in the error recovery mode, so just record it and move on
LOGGER.log(Level.INFO, "Failed to terminate the severed connection",x);
} catch (InterruptedException x) {
// process the interrupt later
Thread.currentThread().interrupt();
}
}
@Override
public synchronized void close() throws IOException {
super.close();
// wait for the child process to complete
try {
proc.join();
} catch (InterruptedException e) {
// process the interrupt later
Thread.currentThread().interrupt();
}
public Channel build(CommandTransport transport) throws IOException {
return new Channel(this,transport) {
/**
* Kill the process when the channel is severed.
*/
@Override
public synchronized void terminate(IOException e) {
super.terminate(e);
try {
proc.kill();
} catch (IOException x) {
// we are already in the error recovery mode, so just record it and move on
LOGGER.log(Level.INFO, "Failed to terminate the severed connection",x);
} catch (InterruptedException x) {
// process the interrupt later
Thread.currentThread().interrupt();
}
}
@Override
public synchronized void join() throws InterruptedException {
super.join();
// wait for the child process to complete, too
try {
proc.join();
} catch (IOException e) {
throw new IOError(e);
}
}
};
}
};
cb.withHeaderStream(header);
for (ChannelConfigurator cc : ChannelConfigurator.all()) {
cc.onChannelBuilding(cb,null); // TODO: what to pass as a context?
}
return cb.build(in,out);
}
public static Channel forProcess(String name, ExecutorService execService, final Process proc, OutputStream header) throws IOException {
final Thread thread = new StreamCopyThread(name + " stderr", proc.getErrorStream(), header);
thread.start();
return new Channel(name, execService, proc.getInputStream(), proc.getOutputStream(), header) {
/**
* Kill the process when the channel is severed.
*/
ChannelBuilder cb = new ChannelBuilder(name,execService) {
@Override
public synchronized void terminate(IOException e) {
super.terminate(e);
proc.destroy();
// the stderr copier should exit by itself
}
@Override
public synchronized void close() throws IOException {
super.close();
// wait for Maven to complete
try {
proc.waitFor();
thread.join();
} catch (InterruptedException e) {
// process the interrupt later
Thread.currentThread().interrupt();
}
public Channel build(CommandTransport transport) throws IOException {
return new Channel(this,transport) {
/**
* Kill the process when the channel is severed.
*/
@Override
public synchronized void terminate(IOException e) {
super.terminate(e);
proc.destroy();
// the stderr copier should exit by itself
}
@Override
public synchronized void join() throws InterruptedException {
super.join();
// wait for the child process to complete, too
proc.waitFor();
thread.join();
}
};
}
};
cb.withHeaderStream(header);
for (ChannelConfigurator cc : ChannelConfigurator.all()) {
cc.onChannelBuilding(cb,null); // TODO: what to pass as a context?
}
return cb.build(proc.getInputStream(),proc.getOutputStream());
}
/**
......
......@@ -23,16 +23,18 @@
*/
package hudson.slaves;
import hudson.model.Computer;
import jenkins.model.Jenkins;
import hudson.model.TaskListener;
import hudson.model.Node;
import hudson.ExtensionPoint;
import hudson.AbortException;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.FilePath;
import hudson.model.Computer;
import hudson.model.Node;
import hudson.model.TaskListener;
import org.jenkinsci.remoting.CallableDecorator;
import hudson.remoting.Channel;
import hudson.AbortException;
import hudson.remoting.ChannelBuilder;
import jenkins.model.Jenkins;
import java.io.IOException;
......@@ -46,6 +48,7 @@ import javax.annotation.Nonnull;
* @since 1.246
*/
public abstract class ComputerListener implements ExtensionPoint {
/**
* Called before a {@link ComputerLauncher} is asked to launch a connection with {@link Computer}.
*
......
......@@ -30,8 +30,8 @@ import hudson.model.Computer;
import hudson.util.TimeUnit2;
import hudson.remoting.VirtualChannel;
import hudson.remoting.Channel;
import hudson.remoting.Callable;
import hudson.Extension;
import jenkins.security.SlaveToMasterCallable;
import java.io.IOException;
import java.util.logging.Logger;
......@@ -102,7 +102,7 @@ public class ConnectionActivityMonitor extends AsyncPeriodicWork {
public boolean enabled = Boolean.getBoolean(ConnectionActivityMonitor.class.getName()+".enabled");
private static final PingCommand PING_COMMAND = new PingCommand();
private static final class PingCommand implements Callable<Void,RuntimeException> {
private static final class PingCommand extends SlaveToMasterCallable<Void,RuntimeException> {
public Void call() throws RuntimeException {
return null;
}
......
......@@ -24,6 +24,8 @@
package hudson.slaves;
import hudson.AbortException;
import hudson.remoting.ChannelBuilder;
import hudson.util.IOUtils;
import hudson.FilePath;
import hudson.Util;
import hudson.model.Computer;
......@@ -34,7 +36,6 @@ import hudson.model.Queue;
import hudson.model.Slave;
import hudson.model.TaskListener;
import hudson.model.User;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.Launcher;
import hudson.remoting.VirtualChannel;
......@@ -46,18 +47,10 @@ import hudson.util.RingBufferLogHandler;
import hudson.util.StreamTaskListener;
import hudson.util.io.ReopenableFileOutputStream;
import hudson.util.io.ReopenableRotatingFileOutputStream;
import jenkins.model.Jenkins;
import jenkins.slaves.EncryptedSlaveAgentJnlpFile;
import jenkins.slaves.JnlpSlaveAgentProtocol;
import jenkins.slaves.systemInfo.SlaveSystemInfo;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import org.apache.commons.io.IOUtils;
import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.WebMethod;
import org.kohsuke.stapler.interceptor.RequirePOST;
......@@ -81,6 +74,14 @@ import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import jenkins.model.Jenkins;
import static hudson.slaves.SlaveComputer.LogHolder.*;
import jenkins.security.ChannelConfigurator;
import jenkins.security.MasterToSlaveCallable;
import jenkins.slaves.JnlpSlaveAgentProtocol;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpRedirect;
/**
......@@ -345,7 +346,15 @@ public class SlaveComputer extends Computer {
* so the implementation of the listener doesn't need to do that again.
*/
public void setChannel(InputStream in, OutputStream out, OutputStream launchLog, Channel.Listener listener) throws IOException, InterruptedException {
Channel channel = new Channel(nodeName,threadPoolForRemoting, Channel.Mode.NEGOTIATE, in,out, launchLog);
ChannelBuilder cb = new ChannelBuilder(nodeName,threadPoolForRemoting)
.withMode(Channel.Mode.NEGOTIATE)
.withHeaderStream(launchLog);
for (ChannelConfigurator cc : ChannelConfigurator.all()) {
cc.onChannelBuilding(cb,this);
}
Channel channel = cb.build(in,out);
setChannel(channel,launchLog,listener);
}
......@@ -393,7 +402,7 @@ public class SlaveComputer extends Computer {
return channel.call(new LoadingTime(true));
}
static class LoadingCount implements Callable<Integer,RuntimeException> {
static class LoadingCount extends MasterToSlaveCallable<Integer,RuntimeException> {
private final boolean resource;
LoadingCount(boolean resource) {
this.resource = resource;
......@@ -404,13 +413,13 @@ public class SlaveComputer extends Computer {
}
}
static class LoadingPrefetchCacheCount implements Callable<Integer,RuntimeException> {
static class LoadingPrefetchCacheCount extends MasterToSlaveCallable<Integer,RuntimeException> {
@Override public Integer call() {
return Channel.current().classLoadingPrefetchCacheCount.get();
}
}
static class LoadingTime implements Callable<Long,RuntimeException> {
static class LoadingTime extends MasterToSlaveCallable<Long,RuntimeException> {
private final boolean resource;
LoadingTime(boolean resource) {
this.resource = resource;
......@@ -677,19 +686,19 @@ public class SlaveComputer extends Computer {
private static final Logger logger = Logger.getLogger(SlaveComputer.class.getName());
private static final class SlaveVersion implements Callable<String,IOException> {
private static final class SlaveVersion extends MasterToSlaveCallable<String,IOException> {
public String call() throws IOException {
try { return Launcher.VERSION; }
catch (Throwable ex) { return "< 1.335"; } // Older slave.jar won't have VERSION
}
}
private static final class DetectOS implements Callable<Boolean,IOException> {
private static final class DetectOS extends MasterToSlaveCallable<Boolean,IOException> {
public Boolean call() throws IOException {
return File.pathSeparatorChar==':';
}
}
private static final class DetectDefaultCharset implements Callable<String,IOException> {
private static final class DetectDefaultCharset extends MasterToSlaveCallable<String,IOException> {
public String call() throws IOException {
return Charset.defaultCharset().name();
}
......@@ -706,7 +715,7 @@ public class SlaveComputer extends Computer {
static final RingBufferLogHandler SLAVE_LOG_HANDLER = new RingBufferLogHandler();
}
private static class SlaveInitializer implements Callable<Void,RuntimeException> {
private static class SlaveInitializer extends MasterToSlaveCallable<Void,RuntimeException> {
public Void call() {
// avoid double installation of the handler. JNLP slaves can reconnect to the master multiple times
// and each connection gets a different RemoteClassLoader, so we need to evict them by class name,
......@@ -760,7 +769,7 @@ public class SlaveComputer extends Computer {
return SlaveSystemInfo.all();
}
private static class SlaveLogFetcher implements Callable<List<LogRecord>,RuntimeException> {
private static class SlaveLogFetcher extends MasterToSlaveCallable<List<LogRecord>,RuntimeException> {
public List<LogRecord> call() {
return new ArrayList<LogRecord>(SLAVE_LOG_HANDLER.getView());
}
......
......@@ -24,6 +24,7 @@
package hudson.tasks;
import hudson.FilePath;
import jenkins.MasterToSlaveFileCallable;
import hudson.Launcher;
import hudson.Util;
import hudson.Extension;
......@@ -247,7 +248,7 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep {
}
}
private static final class ListFiles implements FilePath.FileCallable<Map<String,String>> {
private static final class ListFiles extends MasterToSlaveFileCallable<Map<String,String>> {
private static final long serialVersionUID = 1;
private final String includes, excludes;
private final boolean defaultExcludes;
......
......@@ -27,7 +27,7 @@ import com.google.common.collect.ImmutableMap;
import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.FilePath.FileCallable;
import jenkins.MasterToSlaveFileCallable;
import hudson.Launcher;
import hudson.Util;
import hudson.model.AbstractBuild;
......@@ -209,7 +209,7 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
final long buildTimestamp = build.getTimeInMillis();
List<Record> records = ws.act(new FileCallable<List<Record>>() {
List<Record> records = ws.act(new MasterToSlaveFileCallable<List<Record>>() {
public List<Record> invoke(File baseDir, VirtualChannel channel) throws IOException {
List<Record> results = new ArrayList<Record>();
......
......@@ -24,13 +24,13 @@
package hudson.tasks;
import hudson.Extension;
import jenkins.MasterToSlaveFileCallable;
import hudson.Launcher;
import hudson.Functions;
import hudson.EnvVars;
import hudson.Util;
import hudson.CopyOnWrite;
import hudson.Launcher.LocalLauncher;
import hudson.FilePath.FileCallable;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
......@@ -42,7 +42,6 @@ import jenkins.mvn.GlobalMavenConfig;
import jenkins.mvn.GlobalSettingsProvider;
import jenkins.mvn.SettingsProvider;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.VirtualChannel;
import hudson.slaves.NodeSpecific;
import hudson.tasks._maven.MavenConsoleAnnotator;
......@@ -59,6 +58,7 @@ import hudson.util.VariableResolver.ByMap;
import hudson.util.VariableResolver.Union;
import hudson.util.FormValidation;
import hudson.util.XStream2;
import jenkins.security.MasterToSlaveCallable;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
......@@ -217,7 +217,7 @@ public class Maven extends Builder {
* Looks for <tt>pom.xlm</tt> or <tt>project.xml</tt> to determine the maven executable
* name.
*/
private static final class DecideDefaultMavenCommand implements FileCallable<String> {
private static final class DecideDefaultMavenCommand extends MasterToSlaveFileCallable<String> {
private static final long serialVersionUID = -2327576423452215146L;
// command line arguments.
private final String arguments;
......@@ -506,7 +506,7 @@ public class Maven extends Builder {
public boolean meetsMavenReqVersion(Launcher launcher, int mavenReqVersion) throws IOException, InterruptedException {
// FIXME using similar stuff as in the maven plugin could be better
// olamy : but will add a dependency on maven in core -> so not so good
String mavenVersion = launcher.getChannel().call(new Callable<String,IOException>() {
String mavenVersion = launcher.getChannel().call(new MasterToSlaveCallable<String,IOException>() {
private static final long serialVersionUID = -4143159957567745621L;
public String call() throws IOException {
......@@ -562,7 +562,7 @@ public class Maven extends Builder {
* Gets the executable path of this maven on the given target system.
*/
public String getExecutable(Launcher launcher) throws IOException, InterruptedException {
return launcher.getChannel().call(new Callable<String,IOException>() {
return launcher.getChannel().call(new MasterToSlaveCallable<String,IOException>() {
private static final long serialVersionUID = 2373163112639943768L;
public String call() throws IOException {
......
......@@ -28,12 +28,12 @@ import hudson.Functions;
import hudson.Util;
import hudson.Extension;
import hudson.model.AbstractProject;
import hudson.remoting.Callable;
import hudson.remoting.VirtualChannel;
import hudson.util.FormValidation;
import java.io.IOException;
import java.io.ObjectStreamException;
import hudson.util.LineEndingConversion;
import jenkins.security.MasterToSlaveCallable;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
......@@ -172,7 +172,7 @@ public class Shell extends CommandInterpreter {
return FormValidation.validateExecutable(value);
}
private static final class Shellinterpreter implements Callable<String, IOException> {
private static final class Shellinterpreter extends MasterToSlaveCallable<String, IOException> {
private static final long serialVersionUID = 1L;
......
......@@ -40,6 +40,7 @@ import hudson.util.FormValidation;
import hudson.util.HttpResponses;
import hudson.util.Secret;
import jenkins.model.Jenkins;
import jenkins.security.MasterToSlaveCallable;
import net.sf.json.JSONObject;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpClient;
......@@ -297,7 +298,7 @@ public class JDKInstaller extends ToolInstaller {
public interface FileSystem {
void delete(String file) throws IOException, InterruptedException;
void chmod(String file,int mode) throws IOException, InterruptedException;
InputStream read(String file) throws IOException;
InputStream read(String file) throws IOException, InterruptedException;
/**
* List sub-directories of the given directory and just return the file name portion.
*/
......@@ -320,7 +321,7 @@ public class JDKInstaller extends ToolInstaller {
$(file).chmod(mode);
}
public InputStream read(String file) throws IOException {
public InputStream read(String file) throws IOException, InterruptedException {
return $(file).read();
}
......@@ -530,7 +531,7 @@ public class JDKInstaller extends ToolInstaller {
throw new DetectionFailedException("Unknown CPU name: "+arch);
}
static class GetCurrentPlatform implements Callable<Platform,DetectionFailedException> {
static class GetCurrentPlatform extends MasterToSlaveCallable<Platform,DetectionFailedException> {
private static final long serialVersionUID = 1L;
public Platform call() throws DetectionFailedException {
return current();
......@@ -592,7 +593,7 @@ public class JDKInstaller extends ToolInstaller {
throw new DetectionFailedException("Unknown CPU architecture: "+arch);
}
static class GetCurrentCPU implements Callable<CPU,DetectionFailedException> {
static class GetCurrentCPU extends MasterToSlaveCallable<CPU,DetectionFailedException> {
private static final long serialVersionUID = 1L;
public CPU call() throws DetectionFailedException {
return current();
......
......@@ -26,7 +26,7 @@ package hudson.tools;
import hudson.Extension;
import hudson.FilePath;
import hudson.FilePath.FileCallable;
import jenkins.MasterToSlaveFileCallable;
import hudson.ProxyConfiguration;
import hudson.Util;
import hudson.Functions;
......@@ -116,7 +116,7 @@ public class ZipExtractionInstaller extends ToolInstaller {
* Sets execute permission on all files, since unzip etc. might not do this.
* Hackish, is there a better way?
*/
static class ChmodRecAPlusX implements FileCallable<Void> {
static class ChmodRecAPlusX extends MasterToSlaveFileCallable<Void> {
private static final long serialVersionUID = 1L;
public Void invoke(File d, VirtualChannel channel) throws IOException {
if(!Functions.isWindows())
......
......@@ -29,14 +29,13 @@ import com.sun.jna.ptr.IntByReference;
import hudson.EnvVars;
import hudson.FilePath;
import hudson.Util;
import jenkins.model.Jenkins;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.VirtualChannel;
import hudson.slaves.SlaveComputer;
import hudson.util.ProcessTree.OSProcess;
import hudson.util.ProcessTreeRemoting.IOSProcess;
import hudson.util.ProcessTreeRemoting.IProcessTree;
import jenkins.security.SlaveToMasterCallable;
import org.jvnet.winp.WinProcess;
import org.jvnet.winp.WinpException;
......@@ -150,7 +149,7 @@ public abstract class ProcessTree implements Iterable<OSProcess>, IProcessTree,
try {
VirtualChannel channelToMaster = SlaveComputer.getChannelToMaster();
if (channelToMaster!=null) {
killers = channelToMaster.call(new Callable<List<ProcessKiller>, IOException>() {
killers = channelToMaster.call(new SlaveToMasterCallable<List<ProcessKiller>, IOException>() {
public List<ProcessKiller> call() throws IOException {
return new ArrayList<ProcessKiller>(ProcessKiller.all());
}
......
......@@ -29,11 +29,11 @@ import hudson.FilePath;
import hudson.Functions;
import jenkins.model.Jenkins;
import hudson.remoting.AsyncFutureImpl;
import hudson.remoting.Callable;
import hudson.remoting.DelegatingCallable;
import hudson.remoting.Future;
import hudson.remoting.VirtualChannel;
import hudson.security.AccessControlled;
import jenkins.security.MasterToSlaveCallable;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.kohsuke.stapler.StaplerRequest;
......@@ -70,7 +70,7 @@ public final class RemotingDiagnostics {
return channel.call(new GetSystemProperties());
}
private static final class GetSystemProperties implements Callable<Map<Object,Object>,RuntimeException> {
private static final class GetSystemProperties extends MasterToSlaveCallable<Map<Object,Object>,RuntimeException> {
public Map<Object,Object> call() {
return new TreeMap<Object,Object>(System.getProperties());
}
......@@ -89,7 +89,7 @@ public final class RemotingDiagnostics {
return channel.callAsync(new GetThreadDump());
}
private static final class GetThreadDump implements Callable<Map<String,String>,RuntimeException> {
private static final class GetThreadDump extends MasterToSlaveCallable<Map<String,String>,RuntimeException> {
public Map<String,String> call() {
Map<String,String> r = new LinkedHashMap<String,String>();
ThreadInfo[] data = Functions.getThreadInfos();
......@@ -108,7 +108,7 @@ public final class RemotingDiagnostics {
return channel.call(new Script(script));
}
private static final class Script implements DelegatingCallable<String,RuntimeException> {
private static final class Script extends MasterToSlaveCallable<String,RuntimeException> implements DelegatingCallable<String,RuntimeException> {
private final String script;
private transient ClassLoader cl;
......@@ -150,7 +150,7 @@ public final class RemotingDiagnostics {
* Obtains the heap dump in an HPROF file.
*/
public static FilePath getHeapDump(VirtualChannel channel) throws IOException, InterruptedException {
return channel.call(new Callable<FilePath, IOException>() {
return channel.call(new MasterToSlaveCallable<FilePath, IOException>() {
public FilePath call() throws IOException {
final File hprof = File.createTempFile("hudson-heapdump", "hprof");
hprof.delete();
......
......@@ -25,9 +25,9 @@ package hudson.util.io;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import jenkins.model.Jenkins;
import jenkins.security.SlaveToMasterCallable;
import org.dom4j.io.SAXReader;
import java.io.IOException;
......@@ -78,7 +78,7 @@ public abstract class ParserConfigurator implements ExtensionPoint, Serializable
if (Jenkins.getInstance()==null) {
Channel ch = Channel.current();
if (ch!=null)
all = ch.call(new Callable<Collection<ParserConfigurator>, IOException>() {
all = ch.call(new SlaveToMasterCallable<Collection<ParserConfigurator>, IOException>() {
private static final long serialVersionUID = -2178106894481500733L;
......
package jenkins;
import hudson.FilePath;
import hudson.remoting.Channel;
import hudson.remoting.ChannelBuilder;
import javax.annotation.CheckForNull;
import java.io.File;
/**
* Inspects {@link FilePath} access from remote channels.
*
* @author Kohsuke Kawaguchi
* @see FilePath
* @since 1.THU
*/
public abstract class FilePathFilter {
/**
* Checks if the given file/directory can be read.
*
* On POSIX, this corresponds to the 'r' permission of the file/directory itself.
*/
public void read(File f) throws SecurityException {}
/**
* Checks if the given file can be written.
*
* On POSIX, this corresponds to the 'w' permission of the file itself.
*/
public void write(File f) throws SecurityException {}
/**
* Checks if the given directory can be created.
*
* On POSIX, this corresponds to the 'w' permission of the parent directory.
*/
public void mkdirs(File f) throws SecurityException {}
/**
* Checks if the given file can be created.
*
* On POSIX, this corresponds to the 'w' permission of the parent directory.
*/
public void create(File f) throws SecurityException {}
/**
* Checks if the given file/directory can be deleted.
*
* On POSIX, this corresponds to the 'w' permission of the parent directory.
*/
public void delete(File f) throws SecurityException {}
/**
* Checks if the metadata of the given file/directory (as opposed to the content) can be accessed.
*
* On POSIX, this corresponds to the 'r' permission of the parent directory.
*/
public void stat(File f) throws SecurityException {}
public final void installTo(ChannelBuilder cb) {
synchronized (cb) {
FilePathFilterAggregator filters = (FilePathFilterAggregator) cb.getProperties().get(FilePathFilterAggregator.KEY);
if (filters==null) {
filters = new FilePathFilterAggregator();
cb.withProperty(FilePathFilterAggregator.KEY,filters);
}
filters.add(this);
}
}
public final void uninstallFrom(Channel ch) {
synchronized (ch) {
FilePathFilterAggregator filters = ch.getProperty(FilePathFilterAggregator.KEY);
if (filters!=null) {
filters.remove(this);
}
}
}
/**
* Returns an {@link FilePathFilter} object that represents all the in-scope filters,
* or null if none is needed.
*/
public static @CheckForNull FilePathFilter current() {
Channel ch = Channel.current();
if (ch==null) return null;
return ch.getProperty(FilePathFilterAggregator.KEY);
}
/**
* Immutable instance that represents the empty filter.
*/
public static final FilePathFilter EMPTY = new FilePathFilterAggregator() {
@Override
public void add(FilePathFilter f) {
// noop
}
@Override
public String toString() {
return "None";
}
};
}
package jenkins;
import hudson.FilePath;
import hudson.remoting.ChannelProperty;
import java.io.File;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Maintains a bundle of {@link FilePathFilter} and implement a hook that broadcasts to all the filters.
*
* Accessible as channel property.
*
* @author Kohsuke Kawaguchi
* @see FilePath
* @since 1.THU
*/
class FilePathFilterAggregator extends FilePathFilter {
private final CopyOnWriteArrayList<FilePathFilter> all = new CopyOnWriteArrayList<FilePathFilter>();
public void add(FilePathFilter f) {
all.add(f);
}
public void remove(FilePathFilter f) {
all.remove(f);
}
public void read(File f) throws SecurityException {
for (FilePathFilter filter : all) {
filter.read(f);
}
}
@Override
public void mkdirs(File f) throws SecurityException {
for (FilePathFilter filter : all) {
filter.mkdirs(f);
}
}
public void write(File f) throws SecurityException {
for (FilePathFilter filter : all) {
filter.write(f);
}
}
public void create(File f) throws SecurityException {
for (FilePathFilter filter : all) {
filter.create(f);
}
}
public void delete(File f) throws SecurityException {
for (FilePathFilter filter : all) {
filter.delete(f);
}
}
public void stat(File f) throws SecurityException {
for (FilePathFilter filter : all) {
filter.stat(f);
}
}
@Override public String toString() {
return "FilePathFilterAggregator" + all;
}
static final ChannelProperty<FilePathFilterAggregator> KEY = new ChannelProperty<FilePathFilterAggregator>(FilePathFilterAggregator.class, "FilePathFilters");
}
package jenkins;
import hudson.FilePath.FileCallable;
import jenkins.security.Roles;
import org.jenkinsci.remoting.RoleChecker;
/**
* {@link FileCallable}s that are meant to be only used on the master.
*
* @since 1.THU
*/
public abstract class MasterToSlaveFileCallable<T> implements FileCallable<T> {
@Override
public void checkRoles(RoleChecker checker) throws SecurityException {
checker.check(this, Roles.SLAVE);
}
private static final long serialVersionUID = 1L;
}
package jenkins;
import hudson.FilePath.FileCallable;
import jenkins.security.Roles;
import org.jenkinsci.remoting.RoleChecker;
/**
* {@link FileCallable}s that can be executed on the master, sent by the slave.
*
* @since 1.THU
*/
public abstract class SlaveToMasterFileCallable<T> implements FileCallable<T> {
@Override
public void checkRoles(RoleChecker checker) throws SecurityException {
checker.check(this, Roles.MASTER);
}
private static final long serialVersionUID = 1L;
}
......@@ -196,6 +196,7 @@ import jenkins.model.ProjectNamingStrategy.DefaultProjectNamingStrategy;
import jenkins.security.ConfidentialKey;
import jenkins.security.ConfidentialStore;
import jenkins.security.SecurityListener;
import jenkins.security.MasterToSlaveCallable;
import jenkins.slaves.WorkspaceLocator;
import jenkins.util.Timer;
import jenkins.util.io.FileBoolean;
......@@ -2034,7 +2035,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
@Override
public Callable<ClockDifference, IOException> getClockDifferenceCallable() {
return new Callable<ClockDifference, IOException>() {
return new MasterToSlaveCallable<ClockDifference, IOException>() {
public ClockDifference call() throws IOException {
return new ClockDifference(0);
}
......
package jenkins.security;
import hudson.Extension;
import hudson.remoting.Callable;
import hudson.remoting.ChannelBuilder;
import org.jenkinsci.remoting.Role;
import org.jenkinsci.remoting.RoleChecker;
import org.jenkinsci.remoting.RoleSensitive;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import javax.annotation.Nonnull;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Inspects {@link Callable}s that run on the master.
*
* @author Kohsuke Kawaguchi
* @since 1.THU
*/
@Restricted(NoExternalUse.class) // used implicitly via listener
public class CallableDirectionChecker extends RoleChecker {
private static final String BYPASS_PROP = CallableDirectionChecker.class.getName()+".allow";
/**
* Switch to disable all the defense mechanism completely.
*
* This is an escape hatch in case the fix breaks something critical, to allow the user
* to keep operation.
*/
public static boolean BYPASS = Boolean.getBoolean(BYPASS_PROP);
/**
* Whitelist of {@link RoleSensitive} class names (mostly {@link Callable} classes) that are allowed
* to pass through.
*/
public static ConcurrentMap<String,Void> BYPASS_CALLABLES = new ConcurrentHashMap<String,Void>();
/**
* Test feature that is meant to replace all logging, and log what would have been violations.
* TODO delete before release
*/
private static final PrintWriter BYPASS_LOG;
static {
String log = System.getProperty(CallableDirectionChecker.class.getName()+".log");
if (log == null) {
BYPASS_LOG = null;
} else {
try {
BYPASS_LOG = new PrintWriter(new OutputStreamWriter(new FileOutputStream(log, true)), true);
} catch (FileNotFoundException x) {
throw new ExceptionInInitializerError(x);
}
}
}
private CallableDirectionChecker() {}
@Override
public void check(RoleSensitive subject, @Nonnull Collection<Role> expected) throws SecurityException {
final String name = subject.getClass().getName();
if (expected.contains(Roles.MASTER)) {
if (BYPASS_LOG == null) {
LOGGER.log(Level.FINE, "Executing {0} is allowed since it is targeted for the master role", name);
}
return; // known to be safe
}
if (BYPASS_LOG != null) {
BYPASS_LOG.println(name);
return;
}
if (isWhitelisted(name)) {
// this subject is dubious, but we are letting it through as per whitelisting
LOGGER.log(Level.FINE, "Explicitly allowing {0} to be sent from slave to master", name);
return;
}
throw new SecurityException("Sending " + name + " from slave to master is prohibited");
}
/**
* Is this subject class name whitelisted?
*/
private boolean isWhitelisted(String name) {
if (BYPASS) // everything is whitelisted
return true;
if (BYPASS_CALLABLES.containsKey(name))
return true;
if (Boolean.getBoolean(BYPASS_PROP+"."+name)) {
BYPASS_CALLABLES.put(name,null);
return true;
}
return false;
}
/**
* Installs {@link CallableDirectionChecker} to every channel.
*/
@Restricted(DoNotUse.class) // impl
@Extension
public static class ChannelConfiguratorImpl extends ChannelConfigurator {
@Override
public void onChannelBuilding(ChannelBuilder builder, Object context) {
// if the big red emergency button is pressed, then we need to disable the defense mechanism,
// including enabling classloading.
if (!BYPASS && BYPASS_LOG == null) {
builder.withRemoteClassLoadingAllowed(false);
}
// In either of the above cases, the check method will return normally, but may log things.
builder.withRoleChecker(new CallableDirectionChecker());
}
}
private static final Logger LOGGER = Logger.getLogger(CallableDirectionChecker.class.getName());
}
package jenkins.security;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.remoting.Channel;
import hudson.remoting.ChannelBuilder;
import hudson.slaves.SlaveComputer;
import jenkins.model.Jenkins;
import javax.annotation.Nullable;
/**
* Intercepts the new creation of {@link Channel} and tweak its configuration.
*
* @author Kohsuke Kawaguchi
* @since 1.THU
*/
public abstract class ChannelConfigurator implements ExtensionPoint {
/**
* Called whenever a new channel is being built.
*
* @param builder
* Configures the newly built channel. The callee
* can call its methods to modify its settings.
* @param context
* The parameter that helps the callee determines what this channel is for.
* Legacy callers do not always provide this information, in which case this value might be null.
*
* Possible known values include:
*
* <dl>
* <dt>{@link SlaveComputer}
* <dd>When a channel is being established to talk to a slave.
* </dl>
*/
public void onChannelBuilding(ChannelBuilder builder, @Nullable Object context) {}
/**
* All the registered {@link ChannelConfigurator}s.
*/
public static ExtensionList<ChannelConfigurator> all() {
return Jenkins.getInstance().getExtensionList(ChannelConfigurator.class);
}
}
/*
* The MIT License
*
* Copyright 2014 Jesse Glick.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.security;
import hudson.Extension;
import hudson.FilePath;
import hudson.model.Computer;
import hudson.model.TaskListener;
import hudson.remoting.Channel;
import hudson.remoting.ChannelBuilder;
import hudson.slaves.ComputerListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.FilePathFilter;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
/**
* Blocks slaves from writing to files on the master by default.
*/
@Restricted(DoNotUse.class) // impl
@Extension public class DefaultFilePathFilter extends ChannelConfigurator {
/**
* Escape hatch to disable this check completely.
*/
public static boolean BYPASS = Boolean.getBoolean(DefaultFilePathFilter.class.getName()+".allow");
private static final PrintWriter BYPASS_LOG; // TODO delete before release
static {
String log = System.getProperty("jenkins.security.DefaultFilePathFilter.log");
if (log == null) {
BYPASS_LOG = null;
} else {
try {
BYPASS_LOG = new PrintWriter(new OutputStreamWriter(new FileOutputStream(log, true)), true);
} catch (FileNotFoundException x) {
throw new ExceptionInInitializerError(x);
}
}
}
private static final Logger LOGGER = Logger.getLogger(DefaultFilePathFilter.class.getName());
@Override
public void onChannelBuilding(ChannelBuilder builder, Object context) {
new FilePathFilter() {
private void op(String op, File f) throws SecurityException {
if (BYPASS_LOG != null) {
BYPASS_LOG.println(op + " " + f);
return;
}
if (BYPASS) {
LOGGER.log(Level.FINE, "slave allowed to {0} {1}", new Object[] {op, f});
} else {
// TODO allow finer-grained control, for example by regexp (or Ant pattern) of relative path inside $JENKINS_HOME
throw new SecurityException("slave may not " + op + " " + f);
}
}
@Override public void read(File f) throws SecurityException {
op("read", f);
}
@Override public void write(File f) throws SecurityException {
op("write", f);
}
@Override public void mkdirs(File f) throws SecurityException {
op("mkdirs", f);
}
@Override public void create(File f) throws SecurityException {
op("create", f);
}
@Override public void delete(File f) throws SecurityException {
op("delete", f);
}
@Override public void stat(File f) throws SecurityException {
op("stat", f);
}
}.installTo(builder);
}
}
package jenkins.security;
import hudson.remoting.Callable;
import org.jenkinsci.remoting.Role;
import org.jenkinsci.remoting.RoleChecker;
import java.util.Collection;
/**
* Convenient {@link Callable} meant to be run on slave.
*
* @author Kohsuke Kawaguchi
* @since 1.THU
*/
public abstract class MasterToSlaveCallable<V, T extends Throwable> implements Callable<V,T> {
@Override
public void checkRoles(RoleChecker checker) throws SecurityException {
checker.check(this,Roles.SLAVE);
}
private static final long serialVersionUID = 1L;
}
package jenkins.security;
import hudson.remoting.Callable;
import org.jenkinsci.remoting.RoleChecker;
/**
* {@link Callable} adapter for situations where Callable is not used for remoting but
* just as a convenient function that has parameterized return value and exception type.
*
* @author Kohsuke Kawaguchi
* @since 1.THU
*/
public abstract class NotReallyRoleSensitiveCallable<V,T extends Throwable> implements Callable<V,T> {
@Override
public void checkRoles(RoleChecker checker) throws SecurityException {
// not meant to be used where this matters
throw new UnsupportedOperationException();
}
}
package jenkins.security;
import org.jenkinsci.remoting.Role;
/**
* Predefined {@link Role}s in Jenkins.
*
* <p>
* In Jenkins, there is really only one interesting role, which is the Jenkins master.
* Slaves, CLI, and Maven processes are all going to load classes from the master,
* which means it accepts anything that the master asks for, and thus they need
* not have any role.
*
* @author Kohsuke Kawaguchi
* @since 1.THU
*/
public class Roles {
/**
* Indicates that a callable runs on masters, requested by slaves/CLI/maven/whatever.
*/
public static final Role MASTER = new Role("master");
/**
* Indicates that a callable is meant to run on slaves.
*
* This isn't used to reject callables to run on the slave, but rather to allow
* the master to promptly reject callables that are really not meant to be run on
* the master (as opposed to ones that do not have that information, which gets
* {@link Role#UNKNOWN})
*/
public static final Role SLAVE = new Role("slave");
private Roles() {}
}
package jenkins.security;
import hudson.remoting.Callable;
import org.jenkinsci.remoting.Role;
import org.jenkinsci.remoting.RoleChecker;
import java.util.Collection;
/**
* Convenient {@link Callable} that are meant to run on the master (sent by slave/CLI/etc).
*
* @author Kohsuke Kawaguchi
* @since 1.THU
*/
public abstract class SlaveToMasterCallable<V, T extends Throwable> implements Callable<V,T> {
@Override
public void checkRoles(RoleChecker checker) throws SecurityException {
checker.check(this,Roles.MASTER);
}
private static final long serialVersionUID = 1L;
}
......@@ -4,11 +4,11 @@ import hudson.Extension;
import hudson.FilePath;
import hudson.model.Computer;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.StandardOutputStream;
import hudson.slaves.ComputerListener;
import hudson.util.jna.GNUCLibrary;
import jenkins.security.MasterToSlaveCallable;
import java.io.File;
import java.io.FileDescriptor;
......@@ -35,7 +35,7 @@ public class StandardOutputSwapper extends ComputerListener {
}
}
private static final class ChannelSwapper implements Callable<Boolean,Exception> {
private static final class ChannelSwapper extends MasterToSlaveCallable<Boolean,Exception> {
public Boolean call() throws Exception {
if (File.pathSeparatorChar==';') return false; // Windows
......
......@@ -3,7 +3,6 @@ package jenkins.slaves.restarter;
import hudson.Extension;
import hudson.model.Computer;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Engine;
import hudson.remoting.EngineListener;
import hudson.remoting.EngineListenerAdapter;
......@@ -19,6 +18,7 @@ import java.util.List;
import java.util.logging.Logger;
import static java.util.logging.Level.*;
import jenkins.security.MasterToSlaveCallable;
/**
* Actual slave restart logic.
......@@ -49,7 +49,7 @@ public class JnlpSlaveRestarterInstaller extends ComputerListener implements Ser
VirtualChannel ch = c.getChannel();
if (ch==null) return; // defensive check
List<SlaveRestarter> effective = ch.call(new Callable<List<SlaveRestarter>, IOException>() {
List<SlaveRestarter> effective = ch.call(new MasterToSlaveCallable<List<SlaveRestarter>, IOException>() {
public List<SlaveRestarter> call() throws IOException {
Engine e = Engine.current();
if (e == null) return null; // not running under Engine
......
......@@ -41,6 +41,8 @@ import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import jenkins.MasterToSlaveFileCallable;
/**
* Abstraction over {@link File}, {@link FilePath}, or other items such as network resources or ZIP entries.
* Assumed to be read-only and makes very limited assumptions, just enough to display content and traverse directories.
......@@ -354,7 +356,11 @@ public abstract class VirtualFile implements Comparable<VirtualFile>, Serializab
}
}
@Override public InputStream open() throws IOException {
return f.read();
try {
return f.read();
} catch (InterruptedException x) {
throw (IOException) new IOException(x.toString()).initCause(x);
}
}
@Override public <V> V run(Callable<V,IOException> callable) throws IOException {
try {
......@@ -364,7 +370,7 @@ public abstract class VirtualFile implements Comparable<VirtualFile>, Serializab
}
}
}
private static final class Scanner implements FilePath.FileCallable<String[]> {
private static final class Scanner extends MasterToSlaveFileCallable<String[]> {
private final String glob;
Scanner(String glob) {
this.glob = glob;
......@@ -381,7 +387,7 @@ public abstract class VirtualFile implements Comparable<VirtualFile>, Serializab
}
}
private static final class Readable implements FilePath.FileCallable<Boolean> {
private static final class Readable extends MasterToSlaveFileCallable<Boolean> {
@Override public Boolean invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
return f.canRead();
}
......
......@@ -29,6 +29,7 @@ import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.util.ProcessTree;
import hudson.util.StreamTaskListener;
import jenkins.security.MasterToSlaveCallable;
import org.apache.commons.io.FileUtils;
import org.jvnet.hudson.test.Bug;
......@@ -73,7 +74,7 @@ public class LauncherTest extends ChannelTestCase {
}
}
private static final Callable<Object,RuntimeException> NOOP = new Callable<Object,RuntimeException>() {
private static final Callable<Object,RuntimeException> NOOP = new MasterToSlaveCallable<Object,RuntimeException>() {
public Object call() throws RuntimeException {
return null;
}
......
......@@ -2,6 +2,7 @@ package hudson.os;
import hudson.remoting.Callable;
import hudson.util.StreamTaskListener;
import jenkins.security.MasterToSlaveCallable;
import java.io.FileOutputStream;
......@@ -10,7 +11,7 @@ import java.io.FileOutputStream;
*/
public class SUTester {
public static void main(String[] args) throws Throwable {
SU.execute(StreamTaskListener.fromStdout(),"kohsuke","bogus",new Callable<Object, Throwable>() {
SU.execute(StreamTaskListener.fromStdout(),"kohsuke","bogus",new MasterToSlaveCallable<Object, Throwable>() {
public Object call() throws Throwable {
System.out.println("Touching /tmp/x");
new FileOutputStream("/tmp/x").close();
......
......@@ -5,6 +5,7 @@ import hudson.remoting.Callable;
import hudson.remoting.VirtualChannel;
import hudson.util.ProcessTree.OSProcess;
import hudson.util.ProcessTree.ProcessCallable;
import jenkins.security.MasterToSlaveCallable;
import java.io.IOException;
import java.io.Serializable;
......@@ -39,7 +40,7 @@ public class ProcessTreeTest extends ChannelTestCase {
t.p.act(new ProcessCallableImpl());
}
private static class MyCallable implements Callable<Tag, IOException>, Serializable {
private static class MyCallable extends MasterToSlaveCallable<Tag, IOException> implements Serializable {
public Tag call() throws IOException {
Tag t = new Tag();
t.tree = ProcessTree.get();
......
......@@ -11,7 +11,7 @@ import java.io.PrintWriter;
* @author Kohsuke Kawaguchi
*/
public class ReopenableRotatingFileOutputStreamTest extends TestCase {
public void testRotation() throws IOException {
public void testRotation() throws IOException, InterruptedException {
File base = File.createTempFile("test", "log");
ReopenableRotatingFileOutputStream os = new ReopenableRotatingFileOutputStream(base,3);
PrintWriter w = new PrintWriter(os,true);
......
......@@ -95,7 +95,7 @@ THE SOFTWARE.
<patch.tracker.serverId>jenkins-jira</patch.tracker.serverId>
<slf4jVersion>1.7.7</slf4jVersion> <!-- < 1.6.x version didn't specify the license (MIT) -->
<maven-plugin.version>2.5</maven-plugin.version>
<maven-plugin.version>2.6.1-SNAPSHOT</maven-plugin.version> <!-- TODO SECURITY-144 -->
<animal.sniffer.skip>${skipTests}</animal.sniffer.skip>
<java.level>6</java.level>
......@@ -173,7 +173,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>remoting</artifactId>
<version>2.45</version>
<version>2.46-SNAPSHOT</version><!-- SECURITY-144 branch -->
</dependency>
<dependency>
......
......@@ -3,13 +3,12 @@ package hudson;
import hudson.Launcher.LocalLauncher;
import hudson.Launcher.RemoteLauncher;
import hudson.Proc.RemoteProc;
import hudson.remoting.Callable;
import hudson.remoting.Future;
import hudson.remoting.Pipe;
import hudson.remoting.VirtualChannel;
import hudson.slaves.DumbSlave;
import hudson.util.IOUtils;
import hudson.util.StreamTaskListener;
import jenkins.security.MasterToSlaveCallable;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.HudsonTestCase;
......@@ -70,7 +69,7 @@ public class ProcTest extends HudsonTestCase {
return ch;
}
private static class ChannelFiller implements Callable<Void,IOException> {
private static class ChannelFiller extends MasterToSlaveCallable<Void,IOException> {
private final OutputStream o;
private ChannelFiller(OutputStream o) {
......
......@@ -23,6 +23,7 @@
*/
package hudson.logging;
import jenkins.security.MasterToSlaveCallable;
import org.jvnet.hudson.test.Url;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
......@@ -109,7 +110,7 @@ public class LogRecorderManagerTest {
assertFalse(text, text.contains("msg #4"));
}
private static final class Log implements Callable<Boolean,Error> {
private static final class Log extends MasterToSlaveCallable<Boolean,Error> {
private final Level level;
private final String logger;
private final String message;
......
......@@ -35,6 +35,7 @@ import hudson.remoting.Callable;
import hudson.remoting.Which;
import hudson.util.ArgumentListBuilder;
import jenkins.security.SlaveToMasterCallable;
import org.jvnet.hudson.test.HudsonTestCase;
import org.jvnet.hudson.test.TestExtension;
......@@ -154,7 +155,7 @@ public class JNLPLauncherTest extends HudsonTestCase {
return c;
}
private static class NoopTask implements Callable<String,RuntimeException> {
private static class NoopTask extends SlaveToMasterCallable<String,RuntimeException> {
public String call() {
return "done";
}
......
/*
* The MIT License
*
* Copyright 2014 Jesse Glick.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.security;
import hudson.FilePath;
import hudson.model.Slave;
import hudson.remoting.Callable;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import org.jenkinsci.remoting.Role;
import org.jenkinsci.remoting.RoleChecker;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.jvnet.hudson.test.JenkinsRule;
public class DefaultFilePathFilterTest {
@Rule public JenkinsRule r = new JenkinsRule();
@Test public void remotePath() throws Exception {
Slave s = r.createOnlineSlave();
FilePath forward = s.getRootPath().child("forward");
forward.write("hello", null);
assertEquals("hello", s.getRootPath().act(new LocalCallable(forward)));
FilePath reverse = new FilePath(new File(r.jenkins.root, "reverse"));
assertFalse(reverse.exists());
try {
s.getChannel().call(new ReverseCallable(reverse));
fail("should have failed");
} catch (SecurityException x) {
// good
// make sure that the stack trace contains the call site info to help assist diagnosis
StringWriter sw = new StringWriter();
x.printStackTrace(new PrintWriter(sw));
assertTrue(sw.toString().contains(DefaultFilePathFilterTest.class.getName()+".remotePath"));
}
assertFalse(reverse.exists());
DefaultFilePathFilter.BYPASS = true;
s.getChannel().call(new ReverseCallable(reverse));
assertTrue(reverse.exists());
assertEquals("goodbye", reverse.readToString());
}
private static class LocalCallable implements Callable<String,Exception> {
private final FilePath p;
LocalCallable(FilePath p) {
this.p = p;
}
@Override public String call() throws Exception {
assertFalse(p.isRemote());
return p.readToString();
}
@Override
public void checkRoles(RoleChecker checker) throws SecurityException {
throw new NoSuchMethodError(); // simulate legacy Callable impls
}
}
private static class ReverseCallable implements Callable<Void,Exception> {
private final FilePath p;
ReverseCallable(FilePath p) {
this.p = p;
}
@Override public Void call() throws Exception {
assertTrue(p.isRemote());
p.write("goodbye", null);
return null;
}
@Override
public void checkRoles(RoleChecker checker) throws SecurityException {
throw new NoSuchMethodError(); // simulate legacy Callable impls
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册