Skip to content

Commit

Permalink
Merge branch 'master' into command-palette
Browse files Browse the repository at this point in the history
  • Loading branch information
janfaracik authored Aug 3, 2024
2 parents 1db14a2 + 4137e49 commit ecbbc8a
Show file tree
Hide file tree
Showing 12 changed files with 399 additions and 223 deletions.
2 changes: 1 addition & 1 deletion ath.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -o xtrace
cd "$(dirname "$0")"

# https://github.com/jenkinsci/acceptance-test-harness/releases
export ATH_VERSION=5895.v44475b_ca_0c78
export ATH_VERSION=5911.v5f88b_6d0c450

if [[ $# -eq 0 ]]; then
export JDK=17
Expand Down
2 changes: 1 addition & 1 deletion bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ THE SOFTWARE.
<properties>
<commons-fileupload2.version>2.0.0-M2</commons-fileupload2.version>
<slf4jVersion>2.0.13</slf4jVersion>
<stapler.version>1881.vd39f3ee5c629</stapler.version>
<stapler.version>1892.v73465f3d074d</stapler.version>
<groovy.version>2.4.21</groovy.version>
</properties>

Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/hudson/model/AdministrativeMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,10 @@ public void doDisable(StaplerRequest req, StaplerResponse rsp) throws IOExceptio
* Form UI elements that change system state, e.g. toggling a feature on or off, need to be hidden from users
* lacking Administer permission.
* </p>
* @since 2.233
* @deprecated Callers should use {@link #checkRequiredPermission()} or {@link #hasRequiredPermission()}.
*/
@Deprecated
public Permission getRequiredPermission() {
return Jenkins.ADMINISTER;
}
Expand All @@ -213,6 +216,7 @@ public Permission getRequiredPermission() {
* </p>
* @see #getRequiredPermission()
* @see #hasRequiredPermission()
* @since 2.468
*/
public void checkRequiredPermission() {
Jenkins.get().checkPermission(getRequiredPermission());
Expand All @@ -226,6 +230,7 @@ public void checkRequiredPermission() {
* </p>
* @see #getRequiredPermission()
* @see #checkRequiredPermission()
* @since 2.468
*/
public boolean hasRequiredPermission() {
return Jenkins.get().hasPermission(getRequiredPermission());
Expand Down
5 changes: 4 additions & 1 deletion core/src/main/java/hudson/model/Descriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,9 @@ public T newInstance(@Nullable StaplerRequest req, @NonNull JSONObject formData)
return verifyNewInstance(bindJSON(req, clazz, formData, true));
}
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | RuntimeException e) {
if (e instanceof RuntimeException && e instanceof HttpResponse) {
throw (RuntimeException) e;
}
throw new LinkageError("Failed to instantiate " + clazz + " from " + RedactSecretJsonInErrorMessageSanitizer.INSTANCE.sanitize(formData), e);
}
}
Expand Down Expand Up @@ -674,7 +677,7 @@ public Object instantiate(Class actualType, JSONObject json) {
+ actualType.getName() + " " + json);
}
} catch (Exception x) {
LOGGER.log(Level.WARNING, "falling back to default instantiation " + actualType.getName() + " " + json, x);
LOGGER.log(x instanceof HttpResponse ? Level.FINE : Level.WARNING, "falling back to default instantiation " + actualType.getName() + " " + json, x);
// If nested objects are not using newInstance, bindJSON will wind up throwing the same exception anyway,
// so logging above will result in a duplicated stack trace.
// However if they *are* then this is the only way to find errors in that newInstance.
Expand Down
157 changes: 156 additions & 1 deletion core/src/main/java/hudson/model/UpdateCenter.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,18 @@
import hudson.util.PersistedList;
import hudson.util.VersionNumber;
import hudson.util.XStream2;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.lang.reflect.Constructor;
import java.net.HttpRetryException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
Expand All @@ -86,6 +90,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
Expand Down Expand Up @@ -1809,6 +1814,83 @@ public void run() {
String getComputedSHA512();
}

@SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_SHA1", justification = "SHA-1 is only used as a fallback if SHA-256/SHA-512 are not available")
private static class FileWithComputedChecksums implements WithComputedChecksums {

private final File file;

private String computedSHA1;
private String computedSHA256;
private String computedSHA512;

FileWithComputedChecksums(File file) {
this.file = Objects.requireNonNull(file);
}

@Override
public synchronized String getComputedSHA1() {
if (computedSHA1 != null) {
return computedSHA1;
}

MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException(e);
}
computedSHA1 = computeDigest(messageDigest);
return computedSHA1;
}

@Override
public synchronized String getComputedSHA256() {
if (computedSHA256 != null) {
return computedSHA256;
}

MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException(e);
}
computedSHA256 = computeDigest(messageDigest);
return computedSHA256;
}

@Override
public synchronized String getComputedSHA512() {
if (computedSHA512 != null) {
return computedSHA512;
}

MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA-512");
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException(e);
}
computedSHA512 = computeDigest(messageDigest);
return computedSHA512;
}

private String computeDigest(MessageDigest digest) {
try (InputStream is = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(is)) {
byte[] buffer = new byte[1024];
int read = bis.read(buffer, 0, buffer.length);
while (read > -1) {
digest.update(buffer, 0, read);
read = bis.read(buffer, 0, buffer.length);
}
return Base64.getEncoder().encodeToString(digest.digest());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}

/**
* Base class for a job that downloads a file from the Jenkins project.
*/
Expand Down Expand Up @@ -2245,7 +2327,24 @@ public void _run() throws IOException, InstallationStatus {
return;
}
try {
super._run();
File cached = getCached(this);
if (cached != null) {
File dst = getDestination();

// A bit naive, but following the corresponding logic in UpdateCenterConfiguration#download...
File tmp = new File(dst.getPath() + ".tmp");
Files.copy(cached.toPath(), tmp.toPath(), StandardCopyOption.REPLACE_EXISTING);

config.postValidate(this, tmp);

/*
* Will unfortunately validate the checksum a second time, but this should still be faster than
* network I/O and at least allows us to reuse code...
*/
config.install(this, tmp, dst);
} else {
super._run();
}

// if this is a bundled plugin, make sure it won't get overwritten
PluginWrapper pw = plugin.getInstalled();
Expand Down Expand Up @@ -2278,6 +2377,62 @@ public void _run() throws IOException, InstallationStatus {
}
}

/**
* If we happen to have the file already in the {@coode WEB-INF/detached-plugins} directory and it happens to
* match the checksum we were expecting, then save ourselves a trip to the download site. This method is
* best-effort, and if anything goes wrong we simply fall back to the standard download path.
*
* @return The cached file, or null for a cache miss
*/
@CheckForNull
private File getCached(DownloadJob job) {
URL src;
try {
/*
* Could make PluginManager#getDetachedLocation public and consume it here, but this method is
* best-effort anyway.
*/
src = Jenkins.get().servletContext.getResource(String.format("/WEB-INF/detached-plugins/%s.hpi", plugin.name));
} catch (MalformedURLException e) {
return null;
}

if (src == null || !"file".equals(src.getProtocol())) {
return null;
}

try {
config.preValidate(this, src);
} catch (IOException e) {
return null;
}

File cached;
try {
cached = new File(src.toURI());
} catch (URISyntaxException e) {
return null;
}

if (!cached.isFile()) {
return null;
}

WithComputedChecksums withComputedChecksums = new FileWithComputedChecksums(cached);
try {
verifyChecksums(withComputedChecksums, plugin, cached);
} catch (IOException | UncheckedIOException | UnsupportedOperationException e) {
return null;
}

// Allow us to reuse UpdateCenter.InstallationJob#replace.
job.computedSHA1 = withComputedChecksums.getComputedSHA1();
job.computedSHA256 = withComputedChecksums.getComputedSHA256();
job.computedSHA512 = withComputedChecksums.getComputedSHA512();

return cached;
}

/**
* Indicates there is another installation job for this plugin
* @since 2.1
Expand Down
Loading

0 comments on commit ecbbc8a

Please sign in to comment.