提交 bdcb2fb2 编写于 作者: J James Nord

Merge remote-tracking branch 'origin/master' into kill-jsr-305

......@@ -895,5 +895,16 @@ THE SOFTWARE.
<findbugs.failOnError>true</findbugs.failOnError>
</properties>
</profile>
<profile>
<id>all-tests</id>
<activation>
<property>
<name>!test</name>
</property>
</activation>
<properties>
<maven.test.redirectTestOutputToFile>true</maven.test.redirectTestOutputToFile>
</properties>
</profile>
</profiles>
</project>
......@@ -3279,7 +3279,7 @@ public final class FilePath implements SerializableOnlyOverRemoting {
return act(new IsDescendant(potentialChildRelativePath));
}
private class IsDescendant extends SecureFileCallable<Boolean> {
private static class IsDescendant extends SecureFileCallable<Boolean> {
private static final long serialVersionUID = 1L;
private String potentialChildRelativePath;
......
......@@ -360,7 +360,7 @@ public final class TcpSlaveAgentListener extends Thread {
}
// This is essentially just to be able to pass the parent thread into the callback, as it can't access it otherwise
private abstract class ConnectionHandlerFailureCallback {
private static abstract class ConnectionHandlerFailureCallback {
private Thread parentThread;
public ConnectionHandlerFailureCallback(Thread parentThread) {
......
......@@ -86,6 +86,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.CheckReturnValue;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import javax.crypto.SecretKey;
......@@ -1205,6 +1206,65 @@ public class Util {
return createFileSet(baseDir,includes,null);
}
private static void tryToDeleteSymlink(@NonNull File symlink) {
if (!symlink.delete()) {
LogRecord record = new LogRecord(Level.FINE, "Failed to delete temporary symlink {0}");
record.setParameters(new Object[]{symlink.getAbsolutePath()});
LOGGER.log(record);
}
}
private static void reportAtomicFailure(@NonNull Path pathForSymlink, @NonNull Exception ex) {
LogRecord record = new LogRecord(Level.FINE, "Failed to atomically create/replace symlink {0}");
record.setParameters(new Object[]{pathForSymlink.toAbsolutePath().toString()});
record.setThrown(ex);
LOGGER.log(record);
}
/**
* Creates a symlink to targetPath at baseDir+symlinkPath.
*
* @param pathForSymlink
* The absolute path of the symlink itself as a path object.
* @param fileForSymlink
* The absolute path of the symlink itself as a file object.
* @param target
* The path that the symlink should point to. Usually relative to the directory of the symlink but may instead be an absolute path.
* @param symlinkPath
* Where to create a symlink in (relative to {@code baseDir})
*
* Returns true on success
*/
@CheckReturnValue
private static boolean createSymlinkAtomic(@NonNull Path pathForSymlink, @NonNull File fileForSymlink, @NonNull Path target, @NonNull String symlinkPath) {
try {
File symlink = File.createTempFile("symtmp", null, fileForSymlink);
tryToDeleteSymlink(symlink);
Path tempSymlinkPath = symlink.toPath();
Files.createSymbolicLink(tempSymlinkPath, target);
try {
Files.move(tempSymlinkPath, pathForSymlink, java.nio.file.StandardCopyOption.ATOMIC_MOVE);
return true;
} catch (
UnsupportedOperationException |
SecurityException |
IOException ex) {
// If we couldn't perform an atomic move or the setup, we fall through to another approach
reportAtomicFailure(pathForSymlink, ex);
}
// If we didn't return after our atomic move, then we want to clean up our symlink
tryToDeleteSymlink(symlink);
} catch (
SecurityException |
InvalidPathException |
UnsupportedOperationException |
IOException ex) {
// We couldn't perform an atomic move or the setup.
reportAtomicFailure(pathForSymlink, ex);
}
return false;
}
/**
* Creates a symlink to targetPath at baseDir+symlinkPath.
* <p>
......@@ -1219,16 +1279,21 @@ public class Util {
*/
public static void createSymlink(@NonNull File baseDir, @NonNull String targetPath,
@NonNull String symlinkPath, @NonNull TaskListener listener) throws InterruptedException {
File fileForSymlink = new File(baseDir, symlinkPath);
try {
Path path = fileToPath(new File(baseDir, symlinkPath));
Path pathForSymlink = fileToPath(fileForSymlink);
Path target = Paths.get(targetPath, MemoryReductionUtil.EMPTY_STRING_ARRAY);
if (createSymlinkAtomic(pathForSymlink, fileForSymlink, target, symlinkPath)) {
return;
}
final int maxNumberOfTries = 4;
final int timeInMillis = 100;
for (int tryNumber = 1; tryNumber <= maxNumberOfTries; tryNumber++) {
Files.deleteIfExists(path);
Files.deleteIfExists(pathForSymlink);
try {
Files.createSymbolicLink(path, target);
Files.createSymbolicLink(pathForSymlink, target);
break;
} catch (FileAlreadyExistsException fileAlreadyExistsException) {
if (tryNumber < maxNumberOfTries) {
......@@ -1249,7 +1314,7 @@ public class Util {
return;
}
PrintStream log = listener.getLogger();
log.printf("ln %s %s failed%n",targetPath, new File(baseDir, symlinkPath));
log.printf("ln %s %s failed%n", targetPath, fileForSymlink);
Functions.printStackTrace(e, log);
}
}
......
......@@ -190,7 +190,7 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy {
}
}
class ServerSideImpl extends PlainCLIProtocol.ServerSide {
static class ServerSideImpl extends PlainCLIProtocol.ServerSide {
private Thread runningThread;
private boolean ready;
private final List<String> args = new ArrayList<>();
......
......@@ -69,7 +69,7 @@ public class WorkspaceSnapshotSCM extends SCM {
/**
* {@link Exception} indicating that the resolution of the job/permalink failed.
*/
private final class ResolvedFailedException extends Exception {
private static final class ResolvedFailedException extends Exception {
private ResolvedFailedException(String message) {
super(message);
}
......
......@@ -127,7 +127,7 @@ public class WindowsInstallerLink extends ManagementLink {
sendError("Installation is already complete",req,rsp);
return;
}
if(!DotNet.isInstalled(2,0)) {
if(!DotNet.isInstalled(4,0) && !DotNet.isInstalled(2,0)) {
sendError(".NET Framework 2.0 or later is required for this feature",req,rsp);
return;
}
......
......@@ -81,9 +81,15 @@ public class HistoricalSecrets {
*/
@Deprecated
/*package*/ static SecretKey getLegacyKey() throws GeneralSecurityException {
String secret = Secret.SECRET;
if(secret==null) return Jenkins.get().getSecretKeyAsAES128();
return Util.toAes128Key(secret);
if (Secret.SECRET != null) {
return Util.toAes128Key(Secret.SECRET);
}
Jenkins j = Jenkins.getInstanceOrNull();
if (j != null) {
return j.getSecretKeyAsAES128();
} else {
return Util.toAes128Key("mock");
}
}
static final String MAGIC = "::::MAGIC::::";
......
......@@ -31,7 +31,6 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import jenkins.util.SystemProperties;
import java.util.Arrays;
import jenkins.model.Jenkins;
import hudson.Util;
import jenkins.security.CryptoConfidentialKey;
import org.kohsuke.stapler.Stapler;
......@@ -289,8 +288,10 @@ public final class Secret implements Serializable {
private static final String PROVIDER = SystemProperties.getString(Secret.class.getName()+".provider");
/**
* For testing only. Override the secret key so that we can test this class without {@link Jenkins}.
* For testing only.
* @deprecated Normally unnecessary.
*/
@Deprecated
/*package*/ static String SECRET = null;
/**
......@@ -303,6 +304,9 @@ public final class Secret implements Serializable {
static {
Stapler.CONVERT_UTILS.register(new org.apache.commons.beanutils.Converter() {
public Secret convert(Class type, Object value) {
if (value == null) {
return null;
}
if (value instanceof Secret) {
return (Secret) value;
}
......
......@@ -327,7 +327,7 @@ public class XStream2 extends XStream {
mapperInjectionPoint.setDelegate(m);
}
final class MapperInjectionPoint extends MapperDelegate {
final static class MapperInjectionPoint extends MapperDelegate {
public MapperInjectionPoint(Mapper wrapped) {
super(wrapped);
}
......
......@@ -30,8 +30,6 @@ import org.jinterop.winreg.JIPolicyHandle;
import org.jinterop.winreg.JIWinRegFactory;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* .NET related code.
......@@ -39,74 +37,233 @@ import java.util.regex.Pattern;
* @author Kohsuke Kawaguchi
*/
public class DotNet {
private static final String PATH10 = "SOFTWARE\\Microsoft\\.NETFramework\\Policy\\v1.0\\3705";
private static final String PATH11 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v1.1.4322";
private static final String PATH20 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v2.0.50727";
private static final String PATH30 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.0\\Setup";
private static final String PATH35 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.5";
private static final String PATH4 = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full";
private static final String VALUE_INSTALL = "Install";
private static final String VALUE_INSTALL_SUCCESS = "InstallSuccess";
private static final String VALUE_RELEASE = "Release";
/**
* Returns true if the .NET framework of the given version (or greater) is installed.
* Returns true if the .NET framework of a compatible version is installed.
*/
public static boolean isInstalled(int major, int minor) {
try {
// see http://support.microsoft.com/?scid=kb;en-us;315291 for the basic algorithm
// observation in my registry shows that the actual key name can be things like "v2.0 SP1"
// or "v2.0.50727", so the regexp is written to accommodate this.
RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly("SOFTWARE\\Microsoft\\.NETFramework");
try {
for( String keyName : key.getSubKeys() ) {
if (matches(keyName, major, minor))
return true;
}
if (major == 4 && minor >= 5) {
return isV45PlusInstalled(minor);
} else if (major == 4 && minor == 0) {
return isV40Installed();
} else if (major == 3 && minor == 5) {
return isV35Installed();
} else if (major == 3 && minor == 0) {
return isV35Installed() || isV30Installed();
} else if (major == 2 && minor == 0) {
return isV35Installed() || isV30Installed() || isV20Installed();
} else if (major == 1 && minor == 1) {
return isV11Installed();
} else if (major == 1 && minor == 0) {
return isV11Installed() || isV10Installed();
} else {
return false;
} finally {
key.dispose();
}
} catch (JnaException e) {
if(e.getErrorCode()==2) // thrown when openReadonly fails because the key doesn't exist.
if (e.getErrorCode() == 2) {
// thrown when openReadonly fails because the key doesn't exist.
return false;
}
throw e;
}
}
private static boolean isV45PlusInstalled(int minor) {
try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH4)) {
return key.getIntValue(VALUE_RELEASE) >= GetV45PlusMinRelease(minor);
}
}
private static boolean isV40Installed() {
try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH4)) {
return key.getIntValue(VALUE_INSTALL) == 1;
}
}
private static boolean isV35Installed() {
try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH35)) {
return key.getIntValue(VALUE_INSTALL) == 1;
}
}
private static boolean isV30Installed() {
try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH30)) {
return key.getIntValue(VALUE_INSTALL_SUCCESS) == 1;
}
}
private static boolean isV20Installed() {
try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH20)) {
return key.getIntValue(VALUE_INSTALL) == 1;
}
}
private static boolean isV11Installed() {
try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH11)) {
return key.getIntValue(VALUE_INSTALL) == 1;
}
}
private static boolean isV10Installed() {
try (RegistryKey key = RegistryKey.LOCAL_MACHINE.openReadonly(PATH10)) {
return key.getStringValue(VALUE_INSTALL) == "1";
}
}
/**
* Returns true if the .NET framework of the given version (or grater) is installed
* on a remote machine.
* Returns true if the .NET framework of a compatible version is installed on a remote machine.
*/
public static boolean isInstalled(int major, int minor, String targetMachine, IJIAuthInfo session) throws JIException, UnknownHostException {
IJIWinReg registry = JIWinRegFactory.getSingleTon().getWinreg(session,targetMachine,true);
JIPolicyHandle hklm=null;
JIPolicyHandle key=null;
IJIWinReg registry = JIWinRegFactory.getSingleTon().getWinreg(session, targetMachine, true);
JIPolicyHandle hklm = null;
try {
hklm = registry.winreg_OpenHKLM();
key = registry.winreg_OpenKey(hklm,"SOFTWARE\\Microsoft\\.NETFramework", IJIWinReg.KEY_READ );
for( int i=0; ; i++ ) {
String keyName = registry.winreg_EnumKey(key,i)[0];
if(matches(keyName,major,minor))
return true;
if (major == 4 && minor >= 5) {
return isV45PlusInstalled(minor, registry, hklm);
} else if (major == 4 && minor == 0) {
return isV40Installed(registry, hklm);
} else if (major == 3 && minor == 5) {
return isV35Installed(registry, hklm);
} else if (major == 3 && minor == 0) {
return isV35Installed(registry, hklm) || isV30Installed(registry, hklm);
} else if (major == 2 && minor == 0) {
return isV35Installed(registry, hklm) || isV30Installed(registry, hklm) || isV20Installed(registry, hklm);
} else if (major == 1 && minor == 1) {
return isV11Installed(registry, hklm);
} else if (major == 1 && minor == 0) {
return isV11Installed(registry, hklm) || isV10Installed(registry, hklm);
} else {
return false;
}
} catch (JIException e) {
if(e.getErrorCode()==2)
return false; // not found
if (e.getErrorCode() == 2) {
// not found
return false;
}
throw e;
} finally {
if(hklm!=null)
if (hklm != null) {
registry.winreg_CloseKey(hklm);
if(key!=null)
registry.winreg_CloseKey(key);
}
registry.closeConnection();
}
}
private static boolean matches(String keyName, int major, int minor) {
Matcher m = VERSION_PATTERN.matcher(keyName);
if(m.matches()) {
int mj = Integer.parseInt(m.group(1));
if(mj>=major) {
int mn = Integer.parseInt(m.group(2));
if(mn>=minor)
return true;
private static boolean isV45PlusInstalled(int minor, IJIWinReg registry, JIPolicyHandle hklm) throws JIException {
JIPolicyHandle key = null;
try {
key = registry.winreg_OpenKey(hklm, PATH4, IJIWinReg.KEY_READ);
return GetIntValue(registry, key, VALUE_RELEASE) >= GetV45PlusMinRelease(minor);
} finally {
if (key != null) {
registry.winreg_CloseKey(key);
}
}
}
private static boolean isV40Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException {
JIPolicyHandle key = null;
try {
key = registry.winreg_OpenKey(hklm, PATH4, IJIWinReg.KEY_READ);
return GetIntValue(registry, key, VALUE_INSTALL) == 1;
} finally {
if (key != null) {
registry.winreg_CloseKey(key);
}
}
}
private static boolean isV35Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException {
JIPolicyHandle key = null;
try {
key = registry.winreg_OpenKey(hklm, PATH35, IJIWinReg.KEY_READ);
return GetIntValue(registry, key, VALUE_INSTALL) == 1;
} finally {
if (key != null) {
registry.winreg_CloseKey(key);
}
}
}
private static boolean isV30Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException {
JIPolicyHandle key = null;
try {
key = registry.winreg_OpenKey(hklm, PATH30, IJIWinReg.KEY_READ);
return GetIntValue(registry, key, VALUE_INSTALL_SUCCESS) == 1;
} finally {
if (key != null) {
registry.winreg_CloseKey(key);
}
}
}
private static boolean isV20Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException {
JIPolicyHandle key = null;
try {
key = registry.winreg_OpenKey(hklm, PATH20, IJIWinReg.KEY_READ);
return GetIntValue(registry, key, VALUE_INSTALL) == 1;
} finally {
if (key != null) {
registry.winreg_CloseKey(key);
}
}
return false;
}
private static final Pattern VERSION_PATTERN = Pattern.compile("v(\\d+)\\.(\\d+).*");
private static boolean isV11Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException {
JIPolicyHandle key = null;
try {
key = registry.winreg_OpenKey(hklm, PATH11, IJIWinReg.KEY_READ);
return GetIntValue(registry, key, VALUE_INSTALL) == 1;
} finally {
if (key != null) {
registry.winreg_CloseKey(key);
}
}
}
private static boolean isV10Installed(IJIWinReg registry, JIPolicyHandle hklm) throws JIException {
JIPolicyHandle key = null;
try {
key = registry.winreg_OpenKey(hklm, PATH10, IJIWinReg.KEY_READ);
return GetStringValue(registry, key, VALUE_INSTALL) == "1";
} finally {
if (key != null) {
registry.winreg_CloseKey(key);
}
}
}
private static int GetIntValue(IJIWinReg registry, JIPolicyHandle key, String name) throws JIException {
return RegistryKey.convertBufferToInt((byte[])registry.winreg_QueryValue(key, name, Integer.BYTES)[1]);
}
private static String GetStringValue(IJIWinReg registry, JIPolicyHandle key, String name) throws JIException {
return RegistryKey.convertBufferToString((byte[])registry.winreg_QueryValue(key, name, Character.BYTES * 2)[1]);
}
private static int GetV45PlusMinRelease(int minor) {
switch (minor) {
case 5:
return 378389;
case 6:
return 393295;
case 7:
return 460798;
case 8:
return 528040;
default:
return Integer.MAX_VALUE;
}
}
}
......@@ -27,7 +27,7 @@ import java.util.TreeSet;
*
* @author Kohsuke Kawaguchi
*/
public class RegistryKey {
public class RegistryKey implements AutoCloseable {
/**
* 32bit Windows key value.
*/
......@@ -64,7 +64,7 @@ public class RegistryKey {
* @throws java.io.UnsupportedEncodingException on error
* @return String
*/
private static String convertBufferToString(byte[] buf) {
static String convertBufferToString(byte[] buf) {
return new String(buf, 0, buf.length - 2, StandardCharsets.UTF_16LE);
}
......@@ -74,7 +74,7 @@ public class RegistryKey {
* @param buf buffer
* @return int
*/
private static int convertBufferToInt(byte[] buf) {
static int convertBufferToInt(byte[] buf) {
return ((buf[0] & 0xff) + ((buf[1] & 0xff) << 8) + ((buf[2] & 0xff) << 16) + ((buf[3] & 0xff) << 24));
}
......@@ -287,6 +287,10 @@ public class RegistryKey {
handle = 0;
}
public void close() {
dispose();
}
//
// Root keys
//
......
......@@ -20,7 +20,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
class FilePathFilterAggregator extends FilePathFilter {
private final CopyOnWriteArrayList<Entry> all = new CopyOnWriteArrayList<>();
private class Entry implements Comparable<Entry> {
private static class Entry implements Comparable<Entry> {
final FilePathFilter filter;
final double ordinal;
......
......@@ -48,6 +48,10 @@ public class GlobalBuildDiscarderConfiguration extends GlobalConfiguration {
return ExtensionList.lookupSingleton(GlobalBuildDiscarderConfiguration.class);
}
public GlobalBuildDiscarderConfiguration() {
load();
}
private final DescribableList<GlobalBuildDiscarderStrategy, GlobalBuildDiscarderStrategyDescriptor> configuredBuildDiscarders =
new DescribableList<>(this, Collections.singletonList(new JobGlobalBuildDiscarderStrategy()));
......
......@@ -10,10 +10,14 @@ import org.kohsuke.MetaInfServices;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -51,6 +55,9 @@ public abstract class ConfidentialStore {
*/
protected abstract @CheckForNull byte[] load(ConfidentialKey key) throws IOException;
// TODO consider promoting to public, and offering a default implementation of randomBytes (via the usual Util.isOverridden binary compat trick)
abstract SecureRandom secureRandom();
/**
* Works like {@link SecureRandom#nextBytes(byte[])}.
*
......@@ -64,7 +71,10 @@ public abstract class ConfidentialStore {
public static @NonNull ConfidentialStore get() {
if (TEST!=null) return TEST.get();
Jenkins j = Jenkins.get();
Jenkins j = Jenkins.getInstanceOrNull();
if (j == null) {
return Mock.INSTANCE;
}
Lookup lookup = j.lookup;
ConfidentialStore cs = lookup.get(ConfidentialStore.class);
if (cs==null) {
......@@ -91,10 +101,60 @@ public abstract class ConfidentialStore {
return cs;
}
/**
* Testing only. Used for testing {@link ConfidentialKey} without {@link Jenkins}
/**
* @deprecated No longer needed.
*/
@Deprecated
/*package*/ static ThreadLocal<ConfidentialStore> TEST = null;
static final class Mock extends ConfidentialStore {
static final Mock INSTANCE = new Mock();
private final SecureRandom rand;
private final Map<String, byte[]> data = new ConcurrentHashMap<>();
Mock() {
// Use a predictable seed to make tests more reproducible.
try {
rand = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException x) {
throw new AssertionError("https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecureRandom", x);
}
rand.setSeed(new byte[] {1, 2, 3, 4});
}
void clear() {
data.clear();
}
@Override
protected void store(ConfidentialKey key, byte[] payload) throws IOException {
LOGGER.fine(() -> "storing " + key.getId() + " " + hudson.Util.getDigestOf(hudson.Util.toHexString(payload)));
data.put(key.getId(), payload);
}
@Override
protected byte[] load(ConfidentialKey key) throws IOException {
byte[] payload = data.get(key.getId());
LOGGER.fine(() -> "loading " + key.getId() + " " + (payload != null ? hudson.Util.getDigestOf(hudson.Util.toHexString(payload)) : "null"));
return payload;
}
@Override
SecureRandom secureRandom() {
return rand;
}
@Override
public byte[] randomBytes(int size) {
byte[] random = new byte[size];
rand.nextBytes(random);
return random;
}
}
private static final Logger LOGGER = Logger.getLogger(ConfidentialStore.class.getName());
}
......@@ -140,6 +140,11 @@ public class DefaultConfidentialStore extends ConfidentialStore {
return new File(rootDir, key.getId());
}
@Override
SecureRandom secureRandom() {
return sr;
}
public byte[] randomBytes(int size) {
byte[] random = new byte[size];
sr.nextBytes(random);
......
......@@ -30,7 +30,6 @@ import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
......@@ -79,7 +78,7 @@ public abstract class RSAConfidentialKey extends ConfidentialKey {
byte[] payload = load();
if (payload == null) {
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(2048, new SecureRandom()); // going beyond 2048 requires crypto extension
gen.initialize(2048, cs.secureRandom()); // going beyond 2048 requires crypto extension
KeyPair keys = gen.generateKeyPair();
priv = (RSAPrivateKey) keys.getPrivate();
pub = (RSAPublicKey) keys.getPublic();
......
......@@ -33,6 +33,7 @@ import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
......@@ -766,6 +767,30 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener {
return parent == null ? super.getResourceAsStream(name) : parent.getResourceAsStream(name);
}
@FunctionalInterface
private interface Converter<T> {
T convert(@NonNull JarFile jarFile, @NonNull JarEntry entry) throws IOException;
}
@CheckForNull
private <T> T storeAndConvert(JarFile jarFile, File file, String resourceName, Converter<T> converter) throws IOException {
if (jarFile == null) {
if (file.exists()) {
jarFile = new JarFile(file);
jarFiles.put(file, jarFile);
} else {
return null;
}
//to eliminate a race condition, retrieve the entry
//that is in the hash table under that filename
jarFile = (JarFile) jarFiles.get(file);
}
JarEntry entry = jarFile.getJarEntry(resourceName);
if (entry == null)
return null;
return converter.convert(jarFile, entry);
}
/**
* Returns an inputstream to a given resource in the given file which may
* either be a directory or a zip file.
......@@ -787,21 +812,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener {
return Files.newInputStream(resource.toPath());
}
} else {
if (jarFile == null) {
if (file.exists()) {
jarFile = new JarFile(file);
jarFiles.put(file, jarFile);
} else {
return null;
}
//to eliminate a race condition, retrieve the entry
//that is in the hash table under that filename
jarFile = (JarFile) jarFiles.get(file);
}
JarEntry entry = jarFile.getJarEntry(resourceName);
if (entry != null) {
return jarFile.getInputStream(entry);
}
return storeAndConvert(jarFile, file, resourceName, JarFile::getInputStream);
}
} catch (Exception e) {
log("Ignoring Exception " + e.getClass().getName() + ": " + e.getMessage()
......@@ -1019,24 +1030,13 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener {
}
}
} else {
if (jarFile == null) {
if (file.exists()) {
jarFile = new JarFile(file);
jarFiles.put(file, jarFile);
} else {
return null;
}
// potential race-condition
jarFile = (JarFile) jarFiles.get(file);
}
JarEntry entry = jarFile.getJarEntry(resourceName);
if (entry != null) {
return storeAndConvert(jarFile, file, resourceName, (jar, entry) -> {
try {
return new URL("jar:" + FILE_UTILS.getFileURL(file) + "!/" + entry);
} catch (MalformedURLException ex) {
return null;
}
}
});
}
} catch (Exception e) {
String msg = "Unable to obtain resource from " + file + ": ";
......
......@@ -22,5 +22,5 @@ f.advanced(){
f.textbox()
}
f.validateButton(title:_("Validate Proxy"),
method:"validateProxy", with:"testUrl,name,port,userName,password,noProxyHost")
method:"validateProxy", with:"testUrl,name,port,userName,secretPassword,noProxyHost")
}
......@@ -37,7 +37,7 @@ import java.io.IOException;
*/
public class BulkChangeTest {
private class Point implements Saveable {
private static class Point implements Saveable {
/**
* Don't actually do any save, but just remember how many the actual I/O would have happened.
*/
......
......@@ -16,7 +16,7 @@ import org.jvnet.hudson.test.Issue;
@SuppressWarnings("unchecked")
public class AbstractItemTest {
private class StubAbstractItem extends AbstractItem {
private static class StubAbstractItem extends AbstractItem {
protected StubAbstractItem() {
// sending in null as parent as I don't care for my current tests
......@@ -91,7 +91,7 @@ public class AbstractItemTest {
assertEquals(displayName, i.getDisplayName());
}
private class NameNotEditableItem extends AbstractItem {
private static class NameNotEditableItem extends AbstractItem {
protected NameNotEditableItem(ItemGroup parent, String name){
super(parent, name);
......
......@@ -11,7 +11,7 @@ import static org.mockito.Mockito.when;
public class EnvironmentContributingActionTest {
class OverrideRun extends InvisibleAction implements EnvironmentContributingAction {
static class OverrideRun extends InvisibleAction implements EnvironmentContributingAction {
private boolean wasCalled = false;
@Override
......@@ -24,7 +24,7 @@ public class EnvironmentContributingActionTest {
}
}
class OverrideAbstractBuild extends InvisibleAction implements EnvironmentContributingAction {
static class OverrideAbstractBuild extends InvisibleAction implements EnvironmentContributingAction {
private boolean wasCalled = false;
@Override
......@@ -38,7 +38,7 @@ public class EnvironmentContributingActionTest {
}
}
class OverrideBoth extends InvisibleAction implements EnvironmentContributingAction {
static class OverrideBoth extends InvisibleAction implements EnvironmentContributingAction {
private boolean wasCalledAbstractBuild = false;
private boolean wasCalledRun = false;
......
......@@ -107,7 +107,7 @@ public class FingerprintCleanupThreadTest {
tempDirectory.toFile().deleteOnExit();
}
private class TestTaskListener implements TaskListener {
private static class TestTaskListener implements TaskListener {
private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
private PrintStream logStream = new PrintStream(outputStream);
......@@ -126,7 +126,6 @@ public class FingerprintCleanupThreadTest {
public TestFingerprintCleanupThread(Fingerprint fingerprintToLoad) throws IOException {
this.fingerprintToLoad = fingerprintToLoad;
return;
}
@Override
......@@ -146,7 +145,7 @@ public class FingerprintCleanupThreadTest {
}
private class TestFingerprint extends Fingerprint {
private static class TestFingerprint extends Fingerprint {
private boolean isAlive = true;
......
......@@ -101,7 +101,7 @@ public class LoadStatisticsTest {
assertThat(LoadStatistics.isModern(LoadStatistics.class), is(false));
}
private class Modern extends LoadStatistics {
private static class Modern extends LoadStatistics {
protected Modern(int initialOnlineExecutors, int initialBusyExecutors) {
super(initialOnlineExecutors, initialBusyExecutors);
......
......@@ -16,7 +16,7 @@ import java.util.Set;
*/
public class CyclicGraphDetectorTest {
private class Edge {
private static class Edge {
String src,dst;
private Edge(String src, String dst) {
......@@ -25,7 +25,7 @@ public class CyclicGraphDetectorTest {
}
}
private class Graph extends ArrayList<Edge> {
private static class Graph extends ArrayList<Edge> {
Graph e(String src, String dst) {
add(new Edge(src,dst));
return this;
......
......@@ -62,7 +62,7 @@ public class IsOverriddenTest {
Util.isOverridden(Base.class, Intermediate.class, "aPrivateMethod");
}
public abstract class Base<T> {
public static abstract class Base<T> {
protected abstract void method();
private void aPrivateMethod() {}
public void setX(T t) {}
......
package hudson.util;
import hudson.Util;
import org.junit.rules.ExternalResource;
import java.security.SecureRandom;
/**
* JUnit rule that cleans that sets a temporary {@link Secret#SECRET} value.
*
* @author Kohsuke Kawaguchi
*/
public class MockSecretRule extends ExternalResource {
private String value;
@Override
protected void before() throws Throwable {
byte[] random = new byte[32];
sr.nextBytes(random);
value = Util.toHexString(random);
Secret.SECRET = value;
}
@Override
protected void after() {
if (!Secret.SECRET.equals(value))
throw new IllegalStateException("Someone tinkered with Secret.SECRET");
Secret.SECRET = null;
}
private static final SecureRandom sr = new SecureRandom();
}
......@@ -22,9 +22,6 @@ import org.junit.rules.TemporaryFolder;
public class SecretRewriterTest {
@Rule
public MockSecretRule mockSecretRule = new MockSecretRule();
@Rule
public ConfidentialStoreRule confidentialStoreRule = new ConfidentialStoreRule();
......
......@@ -43,9 +43,6 @@ public class SecretTest {
@Rule
public ConfidentialStoreRule confidentialStore = new ConfidentialStoreRule();
@Rule
public MockSecretRule mockSecretRule = new MockSecretRule();
private static final Pattern ENCRYPTED_VALUE_PATTERN = Pattern.compile("\\{?[A-Za-z0-9+/]+={0,2}}?");
@Test
......
......@@ -136,7 +136,7 @@ public class TarArchiverTest {
t1.join();
}
private class GrowingFileRunnable implements Runnable {
private static class GrowingFileRunnable implements Runnable {
private boolean finish = false;
private Exception ex = null;
private File file;
......
......@@ -2,28 +2,15 @@ package jenkins.security;
import org.junit.rules.ExternalResource;
import org.junit.rules.TemporaryFolder;
/**
* Test rule that injects a temporary {@link DefaultConfidentialStore}
* @author Kohsuke Kawaguchi
* Test rule that makes {@link ConfidentialStore#get} be reset for each test.
*/
public class ConfidentialStoreRule extends ExternalResource {
private final TemporaryFolder tmp = new TemporaryFolder();
@Override
protected void before() throws Throwable {
tmp.create();
ConfidentialStore.TEST.set(new DefaultConfidentialStore(tmp.getRoot()));
ConfidentialStore.Mock.INSTANCE.clear();
}
@Override
protected void after() {
ConfidentialStore.TEST.set(null);
tmp.delete();
}
static {
ConfidentialStore.TEST = new ThreadLocal<>();
}
}
......@@ -84,7 +84,7 @@ public class MarkFindingOutputStreamTest {
m.write(s.charAt(i));
}
class MarkCountingOutputStream extends MarkFindingOutputStream {
static class MarkCountingOutputStream extends MarkFindingOutputStream {
int count = 0;
MarkCountingOutputStream(OutputStream base) {
......
......@@ -76,7 +76,7 @@ THE SOFTWARE.
</issueManagement>
<properties>
<revision>2.229</revision>
<revision>2.230</revision>
<changelist>-SNAPSHOT</changelist>
<!-- *.html files are in UTF-8, and *.properties are in iso-8859-1, so this configuration is actually incorrect,
......
......@@ -56,7 +56,7 @@ public class ExtensionListListenerTest {
Assert.assertEquals(1, listListener.onChangeCallCount);
}
private class MyExtensionListListener extends ExtensionListListener {
private static class MyExtensionListListener extends ExtensionListListener {
private int onChangeCallCount = 0;
@Override
public void onChange() {
......
......@@ -99,7 +99,7 @@ public class ApiSEC1704Test {
}
@ExportedBean
class CustomData {
static class CustomData {
private String secret;
CustomData(String secret){
this.secret = secret;
......
......@@ -124,7 +124,7 @@ public class UpdateCenterConnectionStatusTest {
Assert.assertEquals(ConnectionStatus.FAILED, job.connectionStates.get(ConnectionStatus.UPDATE_SITE));
}
private class TestConfig extends UpdateCenter.UpdateCenterConfiguration {
private static class TestConfig extends UpdateCenter.UpdateCenterConfiguration {
private IOException checkConnectionException;
private IOException checkUpdateCenterException;
......
......@@ -72,7 +72,7 @@ public class SecurityRealmTest {
assertThat(response.getResponseHeaderValue("Expires"), is("0"));
}
private class DummyCaptcha extends CaptchaSupport {
private static class DummyCaptcha extends CaptchaSupport {
@Override
public boolean validateCaptcha(String id, String text) {
return false;
......
......@@ -201,7 +201,7 @@ public class WhoAmITest {
)));
}
private class SecurityRealmImpl extends AbstractPasswordBasedSecurityRealm {
private static class SecurityRealmImpl extends AbstractPasswordBasedSecurityRealm {
@Override
protected UserDetails authenticate(String username, String password) throws AuthenticationException {
......
......@@ -136,7 +136,7 @@ public class ZipExtractionInstallerTest {
assertThat(lastRequest.getResponseText(), containsString(Messages.ZipExtractionInstaller_malformed_url()));
}
private class SpyingJavaScriptEngine extends JavaScriptEngine {
private static class SpyingJavaScriptEngine extends JavaScriptEngine {
private List<XMLHttpRequest> storedRequests = new ArrayList<>();
private String urlToMatch;
private HttpMethod method;
......
......@@ -103,7 +103,7 @@ public class WebSocketAgentsTest {
assertNotNull(s.getChannel().call(new FatTask()));
FreeStyleProject p = r.createFreeStyleProject();
p.setAssignedNode(s);
p.getBuildersList().add(Functions.isWindows() ? new BatchFile("echo hello") : new Shell("dd if=/dev/random count=1024 bs=200"));
p.getBuildersList().add(Functions.isWindows() ? new BatchFile("echo hello") : new Shell("echo hello"));
r.buildAndAssertSuccess(p);
s.toComputer().getLogText().writeLogTo(0, System.out);
} finally {
......
......@@ -5,15 +5,35 @@ import hudson.model.FreeStyleProject;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.tasks.LogRotator;
import hudson.util.DescribableList;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.recipes.LocalData;
public class GlobalBuildDiscarderTest {
@Rule
public JenkinsRule j = new JenkinsRule();
@Test
@LocalData
@Issue("JENKINS-61688")
public void testLoading() throws Exception {
Assert.assertEquals(0, GlobalBuildDiscarderConfiguration.get().getConfiguredBuildDiscarders().size());
}
@Test
@LocalData
@Issue("JENKINS-61688")
public void testLoadingWithDiscarders() throws Exception {
final DescribableList<GlobalBuildDiscarderStrategy, GlobalBuildDiscarderStrategyDescriptor> configuredBuildDiscarders = GlobalBuildDiscarderConfiguration.get().getConfiguredBuildDiscarders();
Assert.assertEquals(2, configuredBuildDiscarders.size());
Assert.assertNotNull(configuredBuildDiscarders.get(JobGlobalBuildDiscarderStrategy.class));
Assert.assertEquals(5, ((LogRotator)configuredBuildDiscarders.get(SimpleGlobalBuildDiscarderStrategy.class).getDiscarder()).getNumToKeep());
}
@Test
public void testJobBuildDiscarder() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
......
......@@ -19,9 +19,12 @@ import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
/**
* @author Kohsuke Kawaguchi
......@@ -81,7 +84,7 @@ public class RekeySecretAdminMonitorTest extends HudsonTestCase {
private void verifyRewrite(File dir) throws Exception {
File xml = new File(dir, "foo.xml");
Pattern pattern = Pattern.compile("<foo>"+plain_regex_match+"</foo>");
assertTrue(pattern.matcher(FileUtils.readFileToString(xml).trim()).matches());
MatcherAssert.assertThat(FileUtils.readFileToString(xml, StandardCharsets.UTF_8).trim(), Matchers.matchesRegex(pattern));
}
// TODO sometimes fails: "Invalid request submission: {json=[Ljava.lang.String;@2c46358e, .crumb=[Ljava.lang.String;@35661457}"
......
......@@ -73,7 +73,7 @@ public class DoActionFilterTest extends StaplerAbstractTest {
private TestAccessModifier getPrivate() {return new TestAccessModifier();}
public class TestAccessModifier {
public static class TestAccessModifier {
@GET
public String doValue() {
return "hello";
......
<?xml version='1.1' encoding='UTF-8'?>
<jenkins.model.GlobalBuildDiscarderConfiguration>
<configuredBuildDiscarders/>
</jenkins.model.GlobalBuildDiscarderConfiguration>
\ No newline at end of file
<?xml version='1.1' encoding='UTF-8'?>
<jenkins.model.GlobalBuildDiscarderConfiguration>
<configuredBuildDiscarders>
<jenkins.model.JobGlobalBuildDiscarderStrategy/>
<jenkins.model.SimpleGlobalBuildDiscarderStrategy>
<discarder class="hudson.tasks.LogRotator">
<daysToKeep>-1</daysToKeep>
<numToKeep>5</numToKeep>
<artifactDaysToKeep>-1</artifactDaysToKeep>
<artifactNumToKeep>-1</artifactNumToKeep>
</discarder>
</jenkins.model.SimpleGlobalBuildDiscarderStrategy>
</configuredBuildDiscarders>
</jenkins.model.GlobalBuildDiscarderConfiguration>
\ No newline at end of file
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* 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.
*/
html {
position: relative;
min-height: 100%;
}
body {
margin: 0;
padding: 0 0 40px 0;
}
/* --------------- header --------------- */
#header {
background-color: #000000;
height: 40px;
}
#header div {
display: inline-block;
height: inherit;
}
#header .logo {
margin-left: 16px;
}
#jenkins-home-link {
position: absolute;
height: 40px;
}
#jenkins-head-icon {
position: absolute;
bottom: 0px;
}
#jenkins-name-icon {
position: absolute;
bottom: 3px;
left: 32px;
}
#header .searchbox, #header .login {
float: right;
padding: 6px 11px;
}
#breadcrumbBar, #footer-container, .top-sticker-inner {
background-color: #f6faf2;
}
/* -------------------------------------- */
#page-body.clear:after {
clear: both;
content: "";
display: table;
}
#side-panel {
padding: 15px 15px 40px 15px;
float: left;
width: 320px;
}
#main-panel {
padding: 15px 15px 40px 15px;
}
body.two-column #main-panel {
margin-left: 320px;
display:block;
}
body.full-screen {
padding: 0;
}
body.full-screen #main-panel {
padding: 0;
}
@media (max-width: 970px) {
body.two-column #side-panel {
width: 100%;
float: none;
padding-bottom: 20px;
}
body.two-column #main-panel {
margin-left: 0;
display:block;
}
}
@media (min-width: 1170px) {
body.two-column #side-panel {
width: 360px;
}
body.two-column #main-panel {
margin-left: 360px;
display:block;
}
}
/* --------------- footer --------------- */
footer {
padding: 11px 0;
background-color: #f6faf2;
border-top: 1px solid #d3d7cf;
border-bottom: 1px solid #f6faf2;
width: 100%;
position: absolute;
bottom: 0;
left: 0;
clear: both;
font-size: 12px;
text-align: right;
}
footer span {
margin-left: 15px;
line-height: 14px;
}
/* -------------------------------------- */
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册