mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-06 16:45:58 +00:00
Merge branch 'master' into suppressStack
This commit is contained in:
188 files changed
+5360
-598
No files matched your search
@@ -431,6 +431,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "Administrator action installing a plugin, which could do far worse.")
|
||||
private static File resolve(File base, String relative) {
|
||||
File rel = new File(relative);
|
||||
if(rel.isAbsolute())
|
||||
|
||||
@@ -2971,7 +2971,7 @@ public final class FilePath implements SerializableOnlyOverRemoting {
|
||||
private static void checkPermissionForValidate() {
|
||||
AccessControlled subject = Stapler.getCurrentRequest().findAncestorObject(AbstractProject.class);
|
||||
if (subject == null)
|
||||
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
|
||||
Jenkins.get().checkPermission(Jenkins.MANAGE);
|
||||
else
|
||||
subject.checkPermission(Item.CONFIGURE);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ package hudson;
|
||||
|
||||
import hudson.model.Slave;
|
||||
import hudson.security.*;
|
||||
import java.util.function.Predicate;
|
||||
import jenkins.telemetry.impl.AutoRefresh;
|
||||
import jenkins.util.SystemProperties;
|
||||
import hudson.cli.CLICommand;
|
||||
@@ -142,6 +143,7 @@ import jenkins.model.Jenkins;
|
||||
import jenkins.model.ModelObjectWithChildren;
|
||||
import jenkins.model.ModelObjectWithContextMenu;
|
||||
|
||||
import org.acegisecurity.AccessDeniedException;
|
||||
import org.apache.commons.jelly.JellyContext;
|
||||
import org.apache.commons.jelly.JellyTagException;
|
||||
import org.apache.commons.jelly.Script;
|
||||
@@ -157,8 +159,6 @@ import org.kohsuke.stapler.StaplerRequest;
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
import org.kohsuke.stapler.jelly.InternationalizedStringExpression.RawHtmlArgument;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import hudson.model.PasswordParameterDefinition;
|
||||
import hudson.util.RunList;
|
||||
|
||||
@@ -186,6 +186,9 @@ public class Functions {
|
||||
private static final AtomicLong iota = new AtomicLong();
|
||||
private static Logger LOGGER = Logger.getLogger(Functions.class.getName());
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
public static /* non-final */ boolean UI_REFRESH = SystemProperties.getBoolean("jenkins.ui.refresh");
|
||||
|
||||
public Functions() {
|
||||
}
|
||||
|
||||
@@ -406,7 +409,7 @@ public class Functions {
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* The head portion is the part of the URL from the {@link jenkins.model.Jenkins}
|
||||
* The head portion is the part of the URL from the {@link Jenkins}
|
||||
* object to the first {@link Run} subtype. When "next/prev build"
|
||||
* is chosen, this part remains intact.
|
||||
*
|
||||
@@ -486,13 +489,23 @@ public class Functions {
|
||||
/**
|
||||
* Gets the system property indicated by the specified key.
|
||||
*
|
||||
* Delegates to {@link SystemProperties#getString(java.lang.String)}.
|
||||
* Delegates to {@link SystemProperties#getString(String)}.
|
||||
*/
|
||||
@Restricted(DoNotUse.class)
|
||||
public static String getSystemProperty(String key) {
|
||||
return SystemProperties.getString(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if and only if the UI refresh is enabled.
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
@Restricted(DoNotUse.class)
|
||||
public static boolean isUiRefreshEnabled() {
|
||||
return UI_REFRESH;
|
||||
}
|
||||
|
||||
public static Map getEnvVars() {
|
||||
return new TreeMap<>(EnvVars.masterEnvVars);
|
||||
}
|
||||
@@ -1051,10 +1064,13 @@ public class Functions {
|
||||
* we needed this for {@link GlobalConfiguration}s are for backward compatibility.
|
||||
*
|
||||
* @param predicate
|
||||
* Filter the descriptors based on {@link GlobalConfigurationCategory}
|
||||
* Filter the descriptors based on this predicate
|
||||
* @since 1.494
|
||||
* @deprecated use {@link #getSortedDescriptorsForGlobalConfigByDescriptor(Predicate)}
|
||||
*/
|
||||
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfig(Predicate<GlobalConfigurationCategory> predicate) {
|
||||
@Deprecated
|
||||
@Restricted(NoExternalUse.class)
|
||||
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfig(com.google.common.base.Predicate<GlobalConfigurationCategory> predicate) {
|
||||
ExtensionList<Descriptor> exts = ExtensionList.lookup(Descriptor.class);
|
||||
List<Tag> r = new ArrayList<>(exts.size());
|
||||
|
||||
@@ -1062,6 +1078,10 @@ public class Functions {
|
||||
Descriptor d = c.getInstance();
|
||||
if (d.getGlobalConfigPage()==null) continue;
|
||||
|
||||
if (!Jenkins.get().hasPermission(d.getRequiredGlobalConfigPagePermission())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (predicate.apply(d.getCategory())) {
|
||||
r.add(new Tag(c.ordinal(), d));
|
||||
}
|
||||
@@ -1075,10 +1095,47 @@ public class Functions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getSortedDescriptorsForGlobalConfig(Predicate)} but with a constant truth predicate, to include all descriptors.
|
||||
* Gets all the descriptors sorted by their inheritance tree of {@link Describable}
|
||||
* so that descriptors of similar types come nearby.
|
||||
*
|
||||
* <p>
|
||||
* We sort them by {@link Extension#ordinal()} but only for {@link GlobalConfiguration}s,
|
||||
* as the value is normally used to compare similar kinds of extensions, and we needed
|
||||
* {@link GlobalConfiguration}s to be able to position themselves in a layer above.
|
||||
* This however creates some asymmetry between regular {@link Descriptor}s and {@link GlobalConfiguration}s.
|
||||
* Perhaps it is better to introduce another annotation element? But then,
|
||||
* extensions shouldn't normally concern themselves about ordering too much, and the only reason
|
||||
* we needed this for {@link GlobalConfiguration}s are for backward compatibility.
|
||||
*
|
||||
* @param predicate
|
||||
* Filter the descriptors based on this predicate
|
||||
* @since TODO
|
||||
*/
|
||||
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfig() {
|
||||
return getSortedDescriptorsForGlobalConfig(Predicates.<GlobalConfigurationCategory>alwaysTrue());
|
||||
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfigByDescriptor(Predicate<Descriptor> predicate) {
|
||||
ExtensionList<Descriptor> exts = ExtensionList.lookup(Descriptor.class);
|
||||
List<Tag> r = new ArrayList<>(exts.size());
|
||||
|
||||
for (ExtensionComponent<Descriptor> c : exts.getComponents()) {
|
||||
Descriptor d = c.getInstance();
|
||||
if (d.getGlobalConfigPage()==null) continue;
|
||||
|
||||
if (predicate.test(d)) {
|
||||
r.add(new Tag(c.ordinal(), d));
|
||||
}
|
||||
}
|
||||
Collections.sort(r);
|
||||
|
||||
List<Descriptor> answer = new ArrayList<>(r.size());
|
||||
for (Tag d : r) answer.add(d.d);
|
||||
|
||||
return DescriptorVisibilityFilter.apply(Jenkins.get(),answer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getSortedDescriptorsForGlobalConfigByDescriptor(Predicate)} but with a constant truth predicate, to include all descriptors.
|
||||
*/
|
||||
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfigByDescriptor() {
|
||||
return getSortedDescriptorsForGlobalConfigByDescriptor(descriptor -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1086,21 +1143,45 @@ public class Functions {
|
||||
*/
|
||||
@Deprecated
|
||||
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfigNoSecurity() {
|
||||
return getSortedDescriptorsForGlobalConfig(Predicates.not(GlobalSecurityConfiguration.FILTER));
|
||||
return getSortedDescriptorsForGlobalConfigByDescriptor(d -> GlobalSecurityConfiguration.FILTER.negate().test(d));
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getSortedDescriptorsForGlobalConfig(Predicate)} but for unclassified descriptors only.
|
||||
* Descriptors in the global configuration form that users with {@link Jenkins#MANAGE} permission can configure.
|
||||
*
|
||||
* @since 1.506
|
||||
*/
|
||||
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfigUnclassified() {
|
||||
return getSortedDescriptorsForGlobalConfig(new Predicate<GlobalConfigurationCategory>() {
|
||||
public boolean apply(GlobalConfigurationCategory cat) {
|
||||
return cat instanceof GlobalConfigurationCategory.Unclassified;
|
||||
}
|
||||
});
|
||||
return getSortedDescriptorsForGlobalConfigByDescriptor(d -> d.getCategory() instanceof GlobalConfigurationCategory.Unclassified && Jenkins.get().hasPermission(d.getRequiredGlobalConfigPagePermission()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Descriptors shown in the global configuration form to users with {@link Jenkins#SYSTEM_READ} permission.
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public static Collection<Descriptor> getSortedDescriptorsForGlobalConfigUnclassifiedReadable() {
|
||||
return getSortedDescriptorsForGlobalConfigByDescriptor(d -> d.getCategory() instanceof GlobalConfigurationCategory.Unclassified && (
|
||||
Jenkins.get().hasPermission(d.getRequiredGlobalConfigPagePermission()) || Jenkins.get().hasPermission(Jenkins.SYSTEM_READ)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current security principal has one of the supplied permissions.
|
||||
*
|
||||
* @throws AccessDeniedException
|
||||
* if the user doesn't have the permission.
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
public static void checkAnyPermission(AccessControlled ac, Permission[] permissions) {
|
||||
if (permissions == null || permissions.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ac.checkAnyPermission(permissions);
|
||||
}
|
||||
|
||||
private static class Tag implements Comparable<Tag> {
|
||||
double ordinal;
|
||||
String hierarchy;
|
||||
|
||||
@@ -1394,7 +1394,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
|
||||
|
||||
@RequirePOST
|
||||
public HttpResponse doUpdateSources(StaplerRequest req) throws IOException {
|
||||
Jenkins.get().checkPermission(CONFIGURE_UPDATECENTER);
|
||||
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
|
||||
|
||||
if (req.hasParameter("remove")) {
|
||||
UpdateCenter uc = Jenkins.get().getUpdateCenter();
|
||||
@@ -1600,7 +1600,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
|
||||
@RequirePOST
|
||||
public HttpResponse doSiteConfigure(@QueryParameter String site) throws IOException {
|
||||
Jenkins hudson = Jenkins.get();
|
||||
hudson.checkPermission(CONFIGURE_UPDATECENTER);
|
||||
hudson.checkPermission(Jenkins.ADMINISTER);
|
||||
UpdateCenter uc = hudson.getUpdateCenter();
|
||||
PersistedList<UpdateSite> sites = uc.getSites();
|
||||
for (UpdateSite s : sites) {
|
||||
@@ -1615,7 +1615,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
|
||||
@POST
|
||||
public HttpResponse doProxyConfigure(StaplerRequest req) throws IOException, ServletException {
|
||||
Jenkins jenkins = Jenkins.get();
|
||||
jenkins.checkPermission(CONFIGURE_UPDATECENTER);
|
||||
jenkins.checkPermission(Jenkins.ADMINISTER);
|
||||
|
||||
ProxyConfiguration pc = req.bindJSON(ProxyConfiguration.class, req.getSubmittedForm());
|
||||
if (pc.name==null) {
|
||||
@@ -1634,7 +1634,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
|
||||
@RequirePOST
|
||||
public HttpResponse doUploadPlugin(StaplerRequest req) throws IOException, ServletException {
|
||||
try {
|
||||
Jenkins.get().checkPermission(UPLOAD_PLUGINS);
|
||||
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
|
||||
|
||||
ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
|
||||
|
||||
@@ -1762,7 +1762,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
|
||||
|
||||
private FormValidation checkUpdatesServer() throws Exception {
|
||||
for (UpdateSite site : Jenkins.get().getUpdateCenter().getSites()) {
|
||||
FormValidation v = site.updateDirectlyNow(DownloadService.signatureCheck);
|
||||
FormValidation v = site.updateDirectlyNow();
|
||||
if (v.kind != FormValidation.Kind.OK) {
|
||||
// Stop with an error
|
||||
return v;
|
||||
@@ -2105,7 +2105,12 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
|
||||
}
|
||||
public static boolean FAST_LOOKUP = !SystemProperties.getBoolean(PluginManager.class.getName()+".noFastLookup");
|
||||
|
||||
/** @deprecated in Jenkins 2.222 use {@link Jenkins#ADMINISTER} instead */
|
||||
@Deprecated
|
||||
public static final Permission UPLOAD_PLUGINS = new Permission(Jenkins.PERMISSIONS, "UploadPlugins", Messages._PluginManager_UploadPluginsPermission_Description(),Jenkins.ADMINISTER,PermissionScope.JENKINS);
|
||||
|
||||
/** @deprecated in Jenkins 2.222 use {@link Jenkins#ADMINISTER} instead */
|
||||
@Deprecated
|
||||
public static final Permission CONFIGURE_UPDATECENTER = new Permission(Jenkins.PERMISSIONS, "ConfigureUpdateCenter", Messages._PluginManager_ConfigureUpdateCenterPermission_Description(),Jenkins.ADMINISTER,PermissionScope.JENKINS);
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
package hudson;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.Launcher.ProcStarter;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.remoting.Channel;
|
||||
@@ -215,6 +216,7 @@ public abstract class Proc {
|
||||
* @param err
|
||||
* null to redirect stderr to stdout.
|
||||
*/
|
||||
@SuppressFBWarnings(value = "COMMAND_INJECTION", justification = "Command injection is the point of this old, barely used class.")
|
||||
public LocalProc(String[] cmd,String[] env,InputStream in,OutputStream out,OutputStream err,File workDir) throws IOException {
|
||||
this( calcName(cmd),
|
||||
stderr(environment(new ProcessBuilder(cmd),env).directory(workDir), err==null || err== SELFPUMP_OUTPUT),
|
||||
|
||||
@@ -589,6 +589,8 @@ public class Util {
|
||||
/**
|
||||
* Computes MD5 digest of the given input stream.
|
||||
*
|
||||
* This method should only be used for non-security applications where the MD5 weakness is not a problem.
|
||||
*
|
||||
* @param source
|
||||
* The stream will be closed by this method at the end of this method.
|
||||
* @return
|
||||
@@ -598,7 +600,7 @@ public class Util {
|
||||
@Nonnull
|
||||
public static String getDigestOf(@Nonnull InputStream source) throws IOException {
|
||||
try {
|
||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
MessageDigest md5 = getMd5();
|
||||
DigestInputStream in = new DigestInputStream(source, md5);
|
||||
// Note: IOUtils.copy() buffers the input internally, so there is no
|
||||
// need to use a BufferedInputStream.
|
||||
@@ -618,6 +620,14 @@ public class Util {
|
||||
*/
|
||||
}
|
||||
|
||||
// TODO JENKINS-60563 remove MD5 from all usages in Jenkins
|
||||
@SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_MD5", justification =
|
||||
"This method should only be used for non-security applications where the MD5 weakness is not a problem.")
|
||||
@Deprecated
|
||||
private static MessageDigest getMd5() throws NoSuchAlgorithmException {
|
||||
return MessageDigest.getInstance("MD5");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static String getDigestOf(@Nonnull String text) {
|
||||
try {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
package hudson.cli;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.remoting.ClassFilter;
|
||||
import hudson.remoting.ObjectInputStreamEx;
|
||||
import hudson.remoting.SocketChannelStream;
|
||||
@@ -113,6 +114,8 @@ public class Connection {
|
||||
/**
|
||||
* Receives an object sent by {@link #writeObject(Object)}
|
||||
*/
|
||||
// TODO JENKINS-60562 remove this class
|
||||
@SuppressFBWarnings(value = "OBJECT_DESERIALIZATION", justification = "Not used. We should just remove it. Class is deprecated.")
|
||||
public <T> T readObject() throws IOException, ClassNotFoundException {
|
||||
ObjectInputStream ois = new ObjectInputStreamEx(in,
|
||||
getClass().getClassLoader(), ClassFilter.DEFAULT);
|
||||
@@ -194,16 +197,20 @@ public class Connection {
|
||||
*/
|
||||
public Connection encryptConnection(SecretKey sessionKey, String algorithm) throws IOException, GeneralSecurityException {
|
||||
Cipher cout = Cipher.getInstance(algorithm);
|
||||
cout.init(Cipher.ENCRYPT_MODE, sessionKey, new IvParameterSpec(sessionKey.getEncoded()));
|
||||
cout.init(Cipher.ENCRYPT_MODE, sessionKey, createIv(sessionKey));
|
||||
CipherOutputStream o = new CipherOutputStream(out, cout);
|
||||
|
||||
Cipher cin = Cipher.getInstance(algorithm);
|
||||
cin.init(Cipher.DECRYPT_MODE, sessionKey, new IvParameterSpec(sessionKey.getEncoded()));
|
||||
cin.init(Cipher.DECRYPT_MODE, sessionKey, createIv(sessionKey));
|
||||
CipherInputStream i = new CipherInputStream(in, cin);
|
||||
|
||||
return new Connection(i,o);
|
||||
}
|
||||
|
||||
private IvParameterSpec createIv(SecretKey sessionKey) {
|
||||
return new IvParameterSpec(sessionKey.getEncoded());
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a byte array that contains arbitrary number of bytes, digests or expands those bits into the specified
|
||||
* number of bytes without loss of entropy.
|
||||
|
||||
@@ -59,7 +59,7 @@ public class GroovyCommand extends CLICommand {
|
||||
|
||||
protected int run() throws Exception {
|
||||
// this allows the caller to manipulate the JVM state, so require the execute script privilege.
|
||||
Jenkins.get().checkPermission(Jenkins.RUN_SCRIPTS);
|
||||
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
|
||||
|
||||
Binding binding = new Binding();
|
||||
binding.setProperty("out",new PrintWriter(stdout,true));
|
||||
|
||||
@@ -60,7 +60,7 @@ public class GroovyshCommand extends CLICommand {
|
||||
@Override
|
||||
protected int run() {
|
||||
// this allows the caller to manipulate the JVM state, so require the admin privilege.
|
||||
Jenkins.get().checkPermission(Jenkins.RUN_SCRIPTS);
|
||||
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
|
||||
|
||||
// this being remote means no jline capability is available
|
||||
System.setProperty("jline.terminal", UnsupportedTerminal.class.getName());
|
||||
|
||||
@@ -83,7 +83,7 @@ public class InstallPluginCommand extends CLICommand {
|
||||
@Override
|
||||
protected int run() throws Exception {
|
||||
Jenkins h = Jenkins.get();
|
||||
h.checkPermission(PluginManager.UPLOAD_PLUGINS);
|
||||
h.checkPermission(Jenkins.ADMINISTER);
|
||||
PluginManager pm = h.getPluginManager();
|
||||
|
||||
if (name != null) {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
package hudson.console;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import jenkins.model.Jenkins;
|
||||
import hudson.remoting.ObjectInputStreamEx;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -128,7 +129,7 @@ public class AnnotatedLargeText<T> extends LargeText {
|
||||
long timestamp = ois.readLong();
|
||||
if (TimeUnit.HOURS.toMillis(1) > abs(System.currentTimeMillis()-timestamp))
|
||||
// don't deserialize something too old to prevent a replay attack
|
||||
return (ConsoleAnnotator) ois.readObject();
|
||||
return getConsoleAnnotator(ois);
|
||||
} catch (RuntimeException ex) {
|
||||
throw new IOException("Could not decode input", ex);
|
||||
}
|
||||
@@ -140,6 +141,11 @@ public class AnnotatedLargeText<T> extends LargeText {
|
||||
return ConsoleAnnotator.initial(context);
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "OBJECT_DESERIALIZATION", justification = "Deserialization is protected by logic.")
|
||||
private ConsoleAnnotator getConsoleAnnotator(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
return (ConsoleAnnotator) ois.readObject();
|
||||
}
|
||||
|
||||
@CheckReturnValue
|
||||
@Override
|
||||
public long writeLogTo(long start, Writer w) throws IOException {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
package hudson.console;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.Functions;
|
||||
import hudson.MarkupText;
|
||||
@@ -275,7 +276,7 @@ public abstract class ConsoleNote<T> implements Serializable, Describable<Consol
|
||||
try (ObjectInputStream ois = new ObjectInputStreamEx(new GZIPInputStream(new ByteArrayInputStream(buf)),
|
||||
jenkins != null ? jenkins.pluginManager.uberClassLoader : ConsoleNote.class.getClassLoader(),
|
||||
ClassFilter.DEFAULT)) {
|
||||
return (ConsoleNote) ois.readObject();
|
||||
return getConsoleNote(ois);
|
||||
}
|
||||
} catch (Error e) {
|
||||
// for example, bogus 'sz' can result in OutOfMemoryError.
|
||||
@@ -284,6 +285,11 @@ public abstract class ConsoleNote<T> implements Serializable, Describable<Consol
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "OBJECT_DESERIALIZATION", justification = "Deserialization is protected by logic.")
|
||||
private static ConsoleNote getConsoleNote(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
return (ConsoleNote) ois.readObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips the encoded console note.
|
||||
*/
|
||||
|
||||
@@ -103,7 +103,7 @@ public final class MemoryUsageMonitor extends PeriodicWork {
|
||||
* Generates the memory usage statistics graph.
|
||||
*/
|
||||
public TrendChart doGraph(@QueryParameter String type) throws IOException {
|
||||
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
|
||||
Jenkins.get().checkPermission(Jenkins.SYSTEM_READ);
|
||||
return MultiStageTimeSeries.createTrendChart(TimeScale.parse(type),used,max);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
|
||||
* If {@link #isActivated()} returns true, Jenkins will use the {@code message.jelly}
|
||||
* view of this object to render the warning text. This happens in the
|
||||
* {@code http://SERVER/jenkins/manage} page. This view should typically render
|
||||
* a DIV box with class='alert alert-error' or class='alert alert-warning' with a human-readable text
|
||||
* a DIV box with class='alert alert-danger' or class='alert alert-warning' with a human-readable text
|
||||
* inside it. It often also contains a link to a page that provides more details
|
||||
* about the problem.
|
||||
* </dd>
|
||||
|
||||
@@ -778,7 +778,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
|
||||
@Exported
|
||||
public Set<LabelAtom> getAssignedLabels() {
|
||||
Node node = getNode();
|
||||
return (node != null) ? node.getAssignedLabels() : Collections.EMPTY_SET;
|
||||
return (node != null) ? node.getAssignedLabels() : Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -786,7 +786,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
|
||||
*/
|
||||
public List<AbstractProject> getTiedJobs() {
|
||||
Node node = getNode();
|
||||
return (node != null) ? node.getSelfLabel().getTiedJobs() : Collections.EMPTY_LIST;
|
||||
return (node != null) ? node.getSelfLabel().getTiedJobs() : Collections.emptyList();
|
||||
}
|
||||
|
||||
public RunList getBuilds() {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
package hudson.model;
|
||||
|
||||
import hudson.DescriptorExtensionList;
|
||||
import hudson.Extension;
|
||||
import hudson.PluginWrapper;
|
||||
import hudson.RelativePath;
|
||||
import hudson.XmlFile;
|
||||
@@ -31,6 +32,7 @@ import hudson.BulkChange;
|
||||
import hudson.ExtensionList;
|
||||
import hudson.Util;
|
||||
import hudson.model.listeners.SaveableListener;
|
||||
import hudson.security.Permission;
|
||||
import hudson.util.FormApply;
|
||||
import hudson.util.FormValidation.CheckMethod;
|
||||
import hudson.util.ReflectionUtils;
|
||||
@@ -830,6 +832,20 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable,
|
||||
return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Unclassified.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the permission type needed in order to access the {@link #getGlobalConfigPage()} for this descriptor.
|
||||
* By default, requires {@link Jenkins#ADMINISTER} permission.
|
||||
* For now this only applies to descriptors configured through the global ({@link jenkins.model.GlobalConfigurationCategory.Unclassified}) configuration.
|
||||
* Override to return something different if appropriate. The only currently supported alternative return value is {@link Jenkins#MANAGE}.
|
||||
*
|
||||
* @return Permission required to globally configure this descriptor.
|
||||
* @since TODO
|
||||
*/
|
||||
public @Nonnull
|
||||
Permission getRequiredGlobalConfigPagePermission() {
|
||||
return Jenkins.ADMINISTER;
|
||||
}
|
||||
|
||||
private String getViewPage(Class<?> clazz, String pageName, String defaultValue) {
|
||||
return getViewPage(clazz,Collections.singleton(pageName),defaultValue);
|
||||
}
|
||||
|
||||
@@ -346,13 +346,12 @@ public final class DirectoryBrowserSupport implements HttpResponse {
|
||||
if(LOGGER.isLoggable(Level.FINE))
|
||||
LOGGER.fine("Serving "+baseFile+" with lastModified=" + lastModified + ", length=" + length);
|
||||
|
||||
InputStream in = baseFile.open();
|
||||
if (view) {
|
||||
// for binary files, provide the file name for download
|
||||
rsp.setHeader("Content-Disposition", "inline; filename=" + baseFile.getName());
|
||||
|
||||
// pseudo file name to let the Stapler set text/plain
|
||||
rsp.serveFile(req, in, lastModified, -1, length, "plain.txt");
|
||||
rsp.serveFile(req, baseFile.open(), lastModified, -1, length, "plain.txt");
|
||||
} else {
|
||||
if (resourceToken != null) {
|
||||
// redirect to second domain
|
||||
@@ -368,7 +367,7 @@ public final class DirectoryBrowserSupport implements HttpResponse {
|
||||
}
|
||||
}
|
||||
}
|
||||
rsp.serveFile(req, in, lastModified, -1, length, baseFile.getName());
|
||||
rsp.serveFile(req, baseFile.open(), lastModified, -1, length, baseFile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1257,8 +1257,7 @@ public class Fingerprint implements ModelObject, Saveable {
|
||||
file.getParentFile().mkdirs();
|
||||
// JENKINS-16301: fast path for the common case.
|
||||
AtomicFileWriter afw = new AtomicFileWriter(file);
|
||||
try {
|
||||
PrintWriter w = new PrintWriter(afw);
|
||||
try (PrintWriter w = new PrintWriter((afw))) {
|
||||
w.println("<?xml version='1.1' encoding='UTF-8'?>");
|
||||
w.println("<fingerprint>");
|
||||
w.print(" <timestamp>");
|
||||
|
||||
@@ -25,9 +25,13 @@ package hudson.model;
|
||||
|
||||
import hudson.model.listeners.ItemListener;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.CheckForNull;
|
||||
import org.acegisecurity.AccessDeniedException;
|
||||
|
||||
@@ -55,6 +59,33 @@ public interface ItemGroup<T extends Item> extends PersistenceRoot, ModelObject
|
||||
*/
|
||||
Collection<T> getItems();
|
||||
|
||||
/**
|
||||
* Gets all the items in this collection in a read-only view
|
||||
* that matches supplied Predicate
|
||||
* @since TODO
|
||||
*/
|
||||
default Collection<T> getItems(Predicate<T> pred) {
|
||||
return getItemsStream(pred)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a read-only stream of all the items in this collection
|
||||
* @since TODO
|
||||
*/
|
||||
default Stream<T> getItemsStream() {
|
||||
return getItems().stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a read-only stream of all the items in this collection
|
||||
* that matches supplied Predicate
|
||||
* @since TODO
|
||||
*/
|
||||
default Stream<T> getItemsStream(Predicate<T> pred) {
|
||||
return getItemsStream().filter(pred);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path relative to the context root,
|
||||
* like "foo/bar/zot/". Note no leading slash but trailing slash.
|
||||
@@ -99,6 +130,14 @@ public interface ItemGroup<T extends Item> extends PersistenceRoot, ModelObject
|
||||
return Items.getAllItems(this, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #getAllItems(Class)} with additional predicate filtering
|
||||
* @since TODO
|
||||
*/
|
||||
default <T extends Item> List<T> getAllItems(Class<T> type, Predicate<T> pred) {
|
||||
return Items.getAllItems(this, type, pred);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the {@link Item}s unordered, lazily and recursively in the {@link ItemGroup} tree
|
||||
* and filter them by the given type.
|
||||
@@ -108,6 +147,15 @@ public interface ItemGroup<T extends Item> extends PersistenceRoot, ModelObject
|
||||
return Items.allItems(this, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the {@link Item}s unordered, lazily and recursively in the {@link ItemGroup} tree
|
||||
* and filter them by the given type and given predicate
|
||||
* @since TODO
|
||||
*/
|
||||
default <T extends Item> Iterable<T> allItems(Class<T> type, Predicate<T> pred) {
|
||||
return Items.allItems(this, type, pred);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the items recursively.
|
||||
* @since 2.93
|
||||
|
||||
@@ -47,6 +47,7 @@ import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Stack;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.function.Predicate;
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nonnull;
|
||||
import jenkins.model.DirectlyModifiableTopLevelItemGroup;
|
||||
@@ -393,26 +394,44 @@ public class Items {
|
||||
* <p>
|
||||
* If you do not need to iterate all items, or if the order of the items is not required, consider using
|
||||
* {@link #allItems(ItemGroup, Class)} instead.
|
||||
*
|
||||
* @param root Root node to start searching from
|
||||
* @param type Given type of of items being searched for
|
||||
* @return List of items matching given criteria
|
||||
*
|
||||
* @since 1.512
|
||||
*/
|
||||
public static <T extends Item> List<T> getAllItems(final ItemGroup root, Class<T> type) {
|
||||
return getAllItems(root ,type, t -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #getAllItems(ItemGroup, Class)} but with a predicate to pre-filter items to
|
||||
* avoid checking ACLs unnecessarily and returning items not required by the caller
|
||||
* @param root Root node to start searching from
|
||||
* @param type Given type of of items being searched for
|
||||
* @param pred Predicate condition to filter items
|
||||
* @return List of items matching given criteria
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
public static <T extends Item> List<T> getAllItems(final ItemGroup root, Class<T> type, Predicate<T> pred) {
|
||||
List<T> r = new ArrayList<>();
|
||||
getAllItems(root, type, r);
|
||||
getAllItems(root, type, r, pred);
|
||||
return r;
|
||||
}
|
||||
private static <T extends Item> void getAllItems(final ItemGroup root, Class<T> type, List<T> r) {
|
||||
List<Item> items = new ArrayList<>(((ItemGroup<?>) root).getItems());
|
||||
private static <T extends Item> void getAllItems(final ItemGroup root, Class<T> type, List<T> r, Predicate<T> pred) {
|
||||
List<Item> items = new ArrayList<>(((ItemGroup<?>) root).getItems(t -> t instanceof ItemGroup || (type.isInstance(t) && pred.test(type.cast(t)))));
|
||||
// because we add items depth first, we can use the quicker BY_NAME comparison
|
||||
items.sort(BY_NAME);
|
||||
for (Item i : items) {
|
||||
if (type.isInstance(i)) {
|
||||
if (type.isInstance(i) && pred.test(type.cast(i))) {
|
||||
if (i.hasPermission(Item.READ)) {
|
||||
r.add(type.cast(i));
|
||||
}
|
||||
}
|
||||
if (i instanceof ItemGroup) {
|
||||
getAllItems((ItemGroup) i, type, r);
|
||||
getAllItems((ItemGroup) i, type, r, pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -434,6 +453,24 @@ public class Items {
|
||||
return allItems(Jenkins.getAuthentication(), root, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a read-only view of all the {@link Item}s recursively matching type and predicate
|
||||
* in the {@link ItemGroup} tree visible to
|
||||
* {@link Jenkins#getAuthentication()} without concern for the order in which items are returned. Each iteration
|
||||
* of the view will be "live" reflecting the items available between the time the iteration was started and the
|
||||
* time the iteration was completed, however if items are moved during an iteration - depending on the move - it
|
||||
* may be possible for such items to escape the entire iteration.
|
||||
*
|
||||
* @param root the root.
|
||||
* @param type the type.
|
||||
* @param <T> the type.
|
||||
* @param <T> the predicate.
|
||||
* @return An {@link Iterable} for all items.
|
||||
* @since TODO
|
||||
*/
|
||||
public static <T extends Item> Iterable<T> allItems(ItemGroup root, Class<T> type, Predicate<T> pred) {
|
||||
return allItems(Jenkins.getAuthentication(), root, type, pred);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a read-only view all the {@link Item}s recursively in the {@link ItemGroup} tree visible to the supplied
|
||||
@@ -449,7 +486,26 @@ public class Items {
|
||||
* @since 2.37
|
||||
*/
|
||||
public static <T extends Item> Iterable<T> allItems(Authentication authentication, ItemGroup root, Class<T> type) {
|
||||
return new AllItemsIterable<>(root, authentication, type);
|
||||
return allItems(authentication, root, type, t -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a read-only view all the {@link Item}s recursively matching supplied type and predicate conditions
|
||||
* in the {@link ItemGroup} tree visible to the supplied
|
||||
* authentication without concern for the order in which items are returned. Each iteration
|
||||
* of the view will be "live" reflecting the items available between the time the iteration was started and the
|
||||
* time the iteration was completed, however if items are moved during an iteration - depending on the move - it
|
||||
* may be possible for such items to escape the entire iteration.
|
||||
*
|
||||
* @param root the root.
|
||||
* @param type the type.
|
||||
* @param <T> the type.
|
||||
* @param pred the predicate.
|
||||
* @return An {@link Iterable} for all items.
|
||||
* @since TODO
|
||||
*/
|
||||
public static <T extends Item> Iterable<T> allItems(Authentication authentication, ItemGroup root, Class<T> type, Predicate<T> pred) {
|
||||
return new AllItemsIterable<>(root, authentication, type, pred);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -518,11 +574,16 @@ public class Items {
|
||||
* The type of item we want to return.
|
||||
*/
|
||||
private final Class<T> type;
|
||||
/**
|
||||
* Predicate to filter items with
|
||||
*/
|
||||
private final Predicate<T> pred;
|
||||
|
||||
private AllItemsIterable(ItemGroup root, Authentication authentication, Class<T> type) {
|
||||
private AllItemsIterable(ItemGroup root, Authentication authentication, Class<T> type, Predicate<T> pred) {
|
||||
this.root = root;
|
||||
this.authentication = authentication;
|
||||
this.type = type;
|
||||
this.pred = pred;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -561,6 +622,7 @@ public class Items {
|
||||
if (next != null) {
|
||||
return true;
|
||||
}
|
||||
Predicate<Item> search = t -> t instanceof ItemGroup || (type.isInstance(t) && pred.test(type.cast(t)));
|
||||
while (true) {
|
||||
if (delegate == null || !delegate.hasNext()) {
|
||||
if (stack.isEmpty()) {
|
||||
@@ -569,13 +631,13 @@ public class Items {
|
||||
ItemGroup group = stack.pop();
|
||||
// group.getItems() is responsible for performing the permission check so we will not repeat it
|
||||
if (Jenkins.getAuthentication() == authentication) {
|
||||
delegate = group.getItems().iterator();
|
||||
delegate = group.getItems(search).iterator();
|
||||
} else {
|
||||
// slower path because the caller has switched authentication
|
||||
// we need to keep the original authentication so that allItems() can be used
|
||||
// like getAllItems() without the cost of building the entire list up front
|
||||
try (ACLContext ctx = ACL.as(authentication)) {
|
||||
delegate = group.getItems().iterator();
|
||||
delegate = group.getItems(search).iterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -584,7 +646,7 @@ public class Items {
|
||||
if (item instanceof ItemGroup) {
|
||||
stack.push((ItemGroup) item);
|
||||
}
|
||||
if (type.isInstance(item)) {
|
||||
if (type.isInstance(item) && pred.test(type.cast(item))) {
|
||||
next = type.cast(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import com.thoughtworks.xstream.converters.Converter;
|
||||
import com.thoughtworks.xstream.converters.MarshallingContext;
|
||||
@@ -388,13 +390,10 @@ public abstract class Label extends Actionable implements Comparable<Label>, Mod
|
||||
*/
|
||||
@Exported
|
||||
public List<AbstractProject> getTiedJobs() {
|
||||
List<AbstractProject> r = new ArrayList<>();
|
||||
for (AbstractProject<?,?> p : Jenkins.get().allItems(AbstractProject.class)) {
|
||||
if(p instanceof TopLevelItem && this.equals(p.getAssignedLabel()))
|
||||
r.add(p);
|
||||
}
|
||||
r.sort(Items.BY_FULL_NAME);
|
||||
return r;
|
||||
return StreamSupport.stream(Jenkins.get().allItems(AbstractProject.class,
|
||||
i -> i instanceof TopLevelItem && this.equals(i.getAssignedLabel())).spliterator(),
|
||||
true)
|
||||
.sorted(Items.BY_FULL_NAME).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.jenkinsci.Symbol;
|
||||
@Extension(ordinal=100) @Symbol("manageJenkins")
|
||||
public class ManageJenkinsAction implements RootAction {
|
||||
public String getIconFileName() {
|
||||
if (Jenkins.get().hasPermission(Jenkins.ADMINISTER))
|
||||
if (Jenkins.get().hasAnyPermission(Jenkins.MANAGE, Jenkins.SYSTEM_READ))
|
||||
return "gear2.png";
|
||||
else
|
||||
return null;
|
||||
|
||||
@@ -112,10 +112,14 @@ public abstract class ManagementLink implements ExtensionPoint, Action {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return permission required for user to access this management link, in addition to {@link Jenkins#ADMINISTER}
|
||||
* Returns the permission required for user to see this management link on the "Manage Jenkins" page ({@link ManageJenkinsAction}).
|
||||
*
|
||||
* Historically, this returned null, which amounted to the same behavior, as {@link Jenkins#ADMINISTER} was required to access the page.
|
||||
*
|
||||
* @return the permission required for the link to be shown on "Manage Jenkins".
|
||||
*/
|
||||
public @CheckForNull Permission getRequiredPermission() {
|
||||
return null;
|
||||
public @Nonnull Permission getRequiredPermission() {
|
||||
return Jenkins.ADMINISTER;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1063,7 +1063,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
|
||||
public List<FormValidation> updateAllSites() throws InterruptedException, ExecutionException {
|
||||
List <Future<FormValidation>> futures = new ArrayList<>();
|
||||
for (UpdateSite site : getSites()) {
|
||||
Future<FormValidation> future = site.updateDirectly(DownloadService.signatureCheck);
|
||||
Future<FormValidation> future = site.updateDirectly();
|
||||
if (future != null) {
|
||||
futures.add(future);
|
||||
}
|
||||
@@ -2516,7 +2516,7 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
|
||||
try {
|
||||
// Need to do the following because the plugin manager will attempt to access
|
||||
// $JENKINS_HOME/updates/$ID_DEFAULT.json. Needs to be up to date.
|
||||
site.updateDirectlyNow(true);
|
||||
site.updateDirectlyNow();
|
||||
} catch (Exception e) {
|
||||
LOGGER.log(WARNING, "Upgrading Jenkins. Failed to update the default Update Site '" + UpdateCenter.ID_DEFAULT +
|
||||
"'. Plugin upgrades may fail.", e);
|
||||
|
||||
@@ -160,6 +160,17 @@ public class UpdateSite {
|
||||
return dataTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the data file from the given URL if the file
|
||||
* does not exist, or is otherwise due for update.
|
||||
* Accepted formats are JSONP or HTML with {@code postMessage}, not raw JSON.
|
||||
* @return null if no updates are necessary, or the future result
|
||||
* @since TODO
|
||||
*/
|
||||
public @CheckForNull Future<FormValidation> updateDirectly() {
|
||||
return updateDirectly(DownloadService.signatureCheck);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the data file from the given URL if the file
|
||||
* does not exist, or is otherwise due for update.
|
||||
@@ -167,7 +178,9 @@ public class UpdateSite {
|
||||
* @param signatureCheck whether to enforce the signature (may be off only for testing!)
|
||||
* @return null if no updates are necessary, or the future result
|
||||
* @since 1.502
|
||||
* @deprecated use {@linkplain #updateDirectly()}
|
||||
*/
|
||||
@Deprecated
|
||||
public @CheckForNull Future<FormValidation> updateDirectly(final boolean signatureCheck) {
|
||||
if (! getDataFile().exists() || isDue()) {
|
||||
return Jenkins.get().getUpdateCenter().updateService.submit(new Callable<FormValidation>() {
|
||||
@@ -180,6 +193,16 @@ public class UpdateSite {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces an update of the data file from the configured URL, irrespective of the last time the data was retrieved.
|
||||
* @return A {@code FormValidation} indicating the if the update metadata was successfully downloaded from the configured update site
|
||||
* @since TODO
|
||||
* @throws IOException if there was an error downloading or saving the file.
|
||||
*/
|
||||
public @Nonnull FormValidation updateDirectlyNow() throws IOException {
|
||||
return updateDirectlyNow(DownloadService.signatureCheck);
|
||||
}
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
public @Nonnull FormValidation updateDirectlyNow(boolean signatureCheck) throws IOException {
|
||||
return updateData(DownloadService.loadJSON(new URL(getUrl() + "?id=" + URLEncoder.encode(getId(), "UTF-8") + "&version=" + URLEncoder.encode(Jenkins.VERSION, "UTF-8"))), signatureCheck);
|
||||
|
||||
@@ -60,9 +60,9 @@ public abstract class ViewJob<JobT extends ViewJob<JobT,RunT>, RunT extends Run<
|
||||
/**
|
||||
* All {@link Run}s. Copy-on-write semantics.
|
||||
*/
|
||||
protected transient /*almost final*/ RunMap<RunT> runs = new RunMap<>();
|
||||
protected transient volatile /*almost final*/ RunMap<RunT> runs = new RunMap<>(null, null);
|
||||
|
||||
private transient boolean notLoaded = true;
|
||||
private transient volatile boolean notLoaded = true;
|
||||
|
||||
/**
|
||||
* If the reloading of runs are in progress (in another thread,
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
*/
|
||||
package hudson.scheduler;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@@ -51,10 +53,10 @@ public abstract class Hash {
|
||||
* Produces an integer in [0,n)
|
||||
*/
|
||||
public abstract int next(int n);
|
||||
|
||||
|
||||
public static Hash from(String seed) {
|
||||
try {
|
||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
MessageDigest md5 = getMd5();
|
||||
md5.update(seed.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] digest = md5.digest();
|
||||
|
||||
@@ -77,6 +79,12 @@ public abstract class Hash {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO JENKINS-60563 remove MD5 from all usages in Jenkins
|
||||
@SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_MD5", justification = "Should not be used for security.")
|
||||
private static MessageDigest getMd5() throws NoSuchAlgorithmException {
|
||||
return MessageDigest.getInstance("MD5");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hash that always return 0.
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,8 @@ import hudson.model.User;
|
||||
import hudson.model.View;
|
||||
import hudson.model.ViewDescriptor;
|
||||
import hudson.model.ViewGroup;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
@@ -69,8 +71,48 @@ public abstract class ACL {
|
||||
if (a == SYSTEM) {
|
||||
return;
|
||||
}
|
||||
if(!hasPermission(a,p))
|
||||
if (!hasPermission(a,p)) {
|
||||
while (!p.enabled && p.impliedBy != null) {
|
||||
p = p.impliedBy;
|
||||
}
|
||||
throw new AccessDeniedException2(a,p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current security principal has one of the supplied permissions.
|
||||
*
|
||||
* This is just a convenience function.
|
||||
*
|
||||
* @throws AccessDeniedException
|
||||
* if the user doesn't have the permission.
|
||||
* @throws IllegalArgumentException
|
||||
* if no permissions are provided
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
public final void checkAnyPermission(@Nonnull Permission... permissions) {
|
||||
if (permissions.length == 0) {
|
||||
throw new IllegalArgumentException("At least one permission must be provided");
|
||||
}
|
||||
|
||||
boolean failed = !hasAnyPermission(permissions);
|
||||
|
||||
Authentication authentication = Jenkins.getAuthentication();
|
||||
if (failed) {
|
||||
String permissionsDisplayName = Arrays.stream(permissions)
|
||||
.map(p -> p.group.title + "/" + p.name)
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
String errorMessage;
|
||||
if (permissions.length == 1) {
|
||||
errorMessage = Messages.AccessDeniedException2_MissingPermission(authentication.getName(), permissionsDisplayName);
|
||||
} else {
|
||||
errorMessage = Messages.AccessDeniedException_MissingPermissions(authentication.getName(), permissionsDisplayName);
|
||||
}
|
||||
|
||||
throw new AccessDeniedException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,6 +129,33 @@ public abstract class ACL {
|
||||
return hasPermission(a, p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current security principal has any of the permissions.
|
||||
*
|
||||
* @return {@code false}
|
||||
* if the user doesn't have one of the required permissions.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* if no permissions are provided
|
||||
*/
|
||||
public final boolean hasAnyPermission(@Nonnull Permission... permissions) {
|
||||
if (permissions.length == 0) {
|
||||
throw new IllegalArgumentException("At least one permission must be provided");
|
||||
}
|
||||
|
||||
Authentication a = Jenkins.getAuthentication();
|
||||
if (a == SYSTEM) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Permission permission : permissions) {
|
||||
if (hasPermission(permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given principle has the given permission.
|
||||
*
|
||||
|
||||
@@ -47,6 +47,16 @@ public interface AccessControlled {
|
||||
getACL().checkPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient short-cut for {@code getACL().checkAnyPermission(permission)}
|
||||
* @see ACL#checkAnyPermission(Permission...)
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
default void checkAnyPermission(@Nonnull Permission... permission) throws AccessDeniedException {
|
||||
getACL().checkAnyPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient short-cut for {@code getACL().hasPermission(permission)}
|
||||
*/
|
||||
@@ -54,6 +64,16 @@ public interface AccessControlled {
|
||||
return getACL().hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient short-cut for {@code getACL().hasAnyPermission(permission)}
|
||||
* @see ACL#hasAnyPermission(Permission...)
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
default boolean hasAnyPermission(@Nonnull Permission... permission) {
|
||||
return getACL().hasAnyPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient short-cut for {@code getACL().hasPermission(a, permission)}
|
||||
* @since 2.92
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
package hudson.security;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.model.User;
|
||||
import jenkins.model.Jenkins;
|
||||
import hudson.util.Scrambler;
|
||||
@@ -162,8 +163,7 @@ public class BasicAuthenticationFilter implements Filter {
|
||||
path += '?'+q;
|
||||
|
||||
// prepare a redirect
|
||||
rsp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
|
||||
rsp.setHeader("Location",path);
|
||||
prepareRedirect(rsp, path);
|
||||
|
||||
// ... but first let the container authenticate this request
|
||||
RequestDispatcher d = servletContext.getRequestDispatcher("/j_security_check?j_username="+
|
||||
@@ -171,6 +171,12 @@ public class BasicAuthenticationFilter implements Filter {
|
||||
d.include(req,rsp);
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "UNVALIDATED_REDIRECT", justification = "Redirect is validated as processed.")
|
||||
private void prepareRedirect(HttpServletResponse rsp, String path) {
|
||||
rsp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
|
||||
rsp.setHeader("Location",path);
|
||||
}
|
||||
|
||||
//public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
// HttpServletRequest req = (HttpServletRequest) request;
|
||||
// String authorization = req.getHeader("Authorization");
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
*/
|
||||
package hudson.security;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import hudson.BulkChange;
|
||||
import hudson.Extension;
|
||||
import hudson.Functions;
|
||||
import hudson.RestrictedSince;
|
||||
import hudson.markup.MarkupFormatter;
|
||||
import hudson.model.Descriptor;
|
||||
import hudson.model.Descriptor.FormException;
|
||||
@@ -37,6 +37,7 @@ import hudson.util.FormApply;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -106,7 +107,7 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr
|
||||
}
|
||||
}
|
||||
|
||||
public boolean configure(StaplerRequest req, JSONObject json) throws hudson.model.Descriptor.FormException {
|
||||
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
|
||||
// for compatibility reasons, the actual value is stored in Jenkins
|
||||
Jenkins j = Jenkins.get();
|
||||
j.checkPermission(Jenkins.ADMINISTER);
|
||||
@@ -126,7 +127,7 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr
|
||||
try {
|
||||
j.setSlaveAgentPort(new ServerTcpPort(json.getJSONObject("slaveAgentPort")).getPort());
|
||||
} catch (IOException e) {
|
||||
throw new hudson.model.Descriptor.FormException(e, "slaveAgentPortType");
|
||||
throw new FormException(e, "slaveAgentPortType");
|
||||
}
|
||||
}
|
||||
Set<String> agentProtocols = new TreeSet<>();
|
||||
@@ -144,7 +145,7 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr
|
||||
|
||||
// persist all the additional security configs
|
||||
boolean result = true;
|
||||
for(Descriptor<?> d : Functions.getSortedDescriptorsForGlobalConfig(FILTER)){
|
||||
for(Descriptor<?> d : Functions.getSortedDescriptorsForGlobalConfigByDescriptor(FILTER)){
|
||||
result &= configureDescriptor(req,json,d);
|
||||
}
|
||||
|
||||
@@ -184,15 +185,13 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr
|
||||
return Jenkins.ADMINISTER;
|
||||
}
|
||||
|
||||
public static Predicate<GlobalConfigurationCategory> FILTER = new Predicate<GlobalConfigurationCategory>() {
|
||||
public boolean apply(GlobalConfigurationCategory input) {
|
||||
return input instanceof GlobalConfigurationCategory.Security;
|
||||
}
|
||||
};
|
||||
@Restricted(NoExternalUse.class)
|
||||
@RestrictedSince("TODO")
|
||||
public static Predicate<Descriptor> FILTER = input -> input.getCategory() instanceof GlobalConfigurationCategory.Security;
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see hudson.model.Describable#getDescriptor()
|
||||
* @see Describable#getDescriptor()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
package hudson.security;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.Functions;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
@@ -100,15 +101,7 @@ public class HudsonAuthenticationEntryPoint extends AuthenticationProcessingFilt
|
||||
} catch (IllegalStateException e) {
|
||||
out = rsp.getWriter();
|
||||
}
|
||||
out.printf(
|
||||
"<html><head>" +
|
||||
"<meta http-equiv='refresh' content='1;url=%1$s'/>" +
|
||||
"<script>window.location.replace('%1$s');</script>" +
|
||||
"</head>" +
|
||||
"<body style='background-color:white; color:white;'>%n" +
|
||||
"%n%n"+
|
||||
"Authentication required%n"+
|
||||
"<!--%n",loginForm);
|
||||
printResponse(loginForm, out);
|
||||
|
||||
if (cause!=null)
|
||||
cause.report(out);
|
||||
@@ -123,4 +116,17 @@ public class HudsonAuthenticationEntryPoint extends AuthenticationProcessingFilt
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "XSS_SERVLET", justification = "Intermediate step for redirecting users to login page.")
|
||||
private void printResponse(String loginForm, PrintWriter out) {
|
||||
out.printf(
|
||||
"<html><head>" +
|
||||
"<meta http-equiv='refresh' content='1;url=%1$s'/>" +
|
||||
"<script>window.location.replace('%1$s');</script>" +
|
||||
"</head>" +
|
||||
"<body style='background-color:white; color:white;'>%n" +
|
||||
"%n%n"+
|
||||
"Authentication required%n"+
|
||||
"<!--%n",loginForm);
|
||||
}
|
||||
}
|
||||
@@ -490,7 +490,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
|
||||
}
|
||||
}
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
@Restricted(NoExternalUse.class) // _entryForm.jelly and signup.jelly
|
||||
public boolean isMailerPluginPresent() {
|
||||
try {
|
||||
// mail support has moved to a separate plugin
|
||||
|
||||
@@ -27,8 +27,11 @@ import hudson.Extension;
|
||||
import jenkins.model.GlobalConfiguration;
|
||||
import jenkins.model.GlobalConfigurationCategory;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.util.SystemProperties;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.jenkinsci.Symbol;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -49,14 +52,29 @@ public class GlobalCrumbIssuerConfiguration extends GlobalConfiguration {
|
||||
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
|
||||
// for compatibility reasons, the actual value is stored in Jenkins
|
||||
Jenkins j = Jenkins.get();
|
||||
if (json.has("csrf")) {
|
||||
JSONObject csrf = json.getJSONObject("csrf");
|
||||
j.setCrumbIssuer(CrumbIssuer.all().newInstanceFromRadioList(csrf, "issuer"));
|
||||
|
||||
if (json.has("crumbIssuer")) {
|
||||
j.setCrumbIssuer(req.bindJSON(CrumbIssuer.class, json.getJSONObject("crumbIssuer")));
|
||||
} else {
|
||||
j.setCrumbIssuer(null);
|
||||
j.setCrumbIssuer(createDefaultCrumbIssuer());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Restricted(NoExternalUse.class) // Jelly
|
||||
public CrumbIssuer getCrumbIssuer() {
|
||||
return Jenkins.get().getCrumbIssuer();
|
||||
}
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
public static CrumbIssuer createDefaultCrumbIssuer() {
|
||||
if (DISABLE_CSRF_PROTECTION) {
|
||||
return null;
|
||||
}
|
||||
return new DefaultCrumbIssuer(SystemProperties.getBoolean(Jenkins.class.getName() + ".crumbIssuerProxyCompatibility", false));
|
||||
}
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
public static /* non-final */ boolean DISABLE_CSRF_PROTECTION = SystemProperties.getBoolean(GlobalCrumbIssuerConfiguration.class.getName() + ".DISABLE_CSRF_PROTECTION");
|
||||
}
|
||||
@@ -46,6 +46,8 @@ import hudson.util.FormValidation;
|
||||
import hudson.util.NamingThreadFactory;
|
||||
import hudson.util.SequentialExecutionQueue;
|
||||
import hudson.util.StreamTaskListener;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -557,7 +559,7 @@ public class SCMTrigger extends Trigger<Item> {
|
||||
if (actions == null) {
|
||||
additionalActions = new Action[0];
|
||||
} else {
|
||||
additionalActions = actions;
|
||||
additionalActions = Arrays.copyOf(actions, actions.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,13 +189,13 @@ public class AtomicFileWriter extends Writer {
|
||||
try {
|
||||
// Try to make an atomic move.
|
||||
Files.move(tmpPath, destPath, StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (IOException e) {
|
||||
} catch (IOException moveFailed) {
|
||||
// If it falls here that can mean many things. Either that the atomic move is not supported,
|
||||
// or something wrong happened. Anyway, let's try to be over-diagnosing
|
||||
if (e instanceof AtomicMoveNotSupportedException) {
|
||||
LOGGER.log(Level.WARNING, "Atomic move not supported. falling back to non-atomic move.", e);
|
||||
if (moveFailed instanceof AtomicMoveNotSupportedException) {
|
||||
LOGGER.log(Level.WARNING, "Atomic move not supported. falling back to non-atomic move.", moveFailed);
|
||||
} else {
|
||||
LOGGER.log(Level.WARNING, "Unable to move atomically, falling back to non-atomic move.", e);
|
||||
LOGGER.log(Level.WARNING, "Unable to move atomically, falling back to non-atomic move.", moveFailed);
|
||||
}
|
||||
|
||||
if (destPath.toFile().exists()) {
|
||||
@@ -204,19 +204,19 @@ public class AtomicFileWriter extends Writer {
|
||||
|
||||
try {
|
||||
Files.move(tmpPath, destPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e1) {
|
||||
e1.addSuppressed(e);
|
||||
} catch (IOException replaceFailed) {
|
||||
replaceFailed.addSuppressed(moveFailed);
|
||||
LOGGER.log(Level.WARNING, "Unable to move {0} to {1}. Attempting to delete {0} and abandoning.",
|
||||
new Path[]{tmpPath, destPath});
|
||||
try {
|
||||
Files.deleteIfExists(tmpPath);
|
||||
} catch (IOException e2) {
|
||||
e2.addSuppressed(e1);
|
||||
} catch (IOException deleteFailed) {
|
||||
replaceFailed.addSuppressed(deleteFailed);
|
||||
LOGGER.log(Level.WARNING, "Unable to delete {0}, good bye then!", tmpPath);
|
||||
throw e2;
|
||||
throw replaceFailed;
|
||||
}
|
||||
|
||||
throw e1;
|
||||
throw replaceFailed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ package hudson.util;
|
||||
import java.lang.RuntimeException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -33,6 +34,7 @@ import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.util.Iterators.DuplicateFilterIterator;
|
||||
|
||||
/**
|
||||
@@ -290,7 +292,7 @@ public class ConsistentHash<T> {
|
||||
*/
|
||||
private int md5(String s) {
|
||||
try {
|
||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
MessageDigest md5 = getMd5();
|
||||
md5.update(s.getBytes());
|
||||
byte[] digest = md5.digest();
|
||||
|
||||
@@ -303,6 +305,12 @@ public class ConsistentHash<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO JENKINS-60563 remove MD5 from all usages in Jenkins
|
||||
@SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_MD5", justification = "Not used for security.")
|
||||
private MessageDigest getMd5() throws NoSuchAlgorithmException {
|
||||
return MessageDigest.getInstance("MD5");
|
||||
}
|
||||
|
||||
/**
|
||||
* unsigned byte->int.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package hudson.util;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.EnvVars;
|
||||
import hudson.Util;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
@@ -17,19 +18,23 @@ class DOSToUnixPathHelper {
|
||||
void validate(File fexe);
|
||||
}
|
||||
static private boolean checkPrefix(String prefix, Helper helper) {
|
||||
File f = new File(prefix);
|
||||
File f = constructFile(prefix);
|
||||
if(f.exists()) {
|
||||
helper.checkExecutable(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
File fexe = new File(prefix+".exe");
|
||||
File fexe = constructFile(prefix+".exe");
|
||||
if(fexe.exists()) {
|
||||
helper.checkExecutable(fexe);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "Limited use for locating shell executable by administrator.")
|
||||
private static File constructFile(String prefix) {
|
||||
return new File(prefix);
|
||||
}
|
||||
static void iteratePath(String exe, Helper helper) {
|
||||
exe = fixEmpty(exe);
|
||||
if(exe==null) {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
package hudson.util;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.FilePath;
|
||||
import hudson.ProxyConfiguration;
|
||||
import hudson.Util;
|
||||
@@ -145,6 +146,7 @@ public abstract class FormFieldValidator {
|
||||
/**
|
||||
* Gets the parameter as a file.
|
||||
*/
|
||||
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "Not used.")
|
||||
protected final File getFileParameter(String paramName) {
|
||||
return new File(Util.fixNull(request.getParameter(paramName)));
|
||||
}
|
||||
@@ -329,7 +331,7 @@ public abstract class FormFieldValidator {
|
||||
|
||||
try {
|
||||
URL url = new URL(value);
|
||||
HttpURLConnection con = (HttpURLConnection)url.openConnection();
|
||||
HttpURLConnection con = openConnection(url);
|
||||
con.connect();
|
||||
if(con.getResponseCode()!=200
|
||||
|| con.getHeaderField("X-Hudson")==null) {
|
||||
@@ -342,6 +344,11 @@ public abstract class FormFieldValidator {
|
||||
handleIOException(value,e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "URLCONNECTION_SSRF_FD", justification = "Not used.")
|
||||
private HttpURLConnection openConnection(URL url) throws IOException {
|
||||
return (HttpURLConnection)url.openConnection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -196,7 +196,7 @@ public final class RemotingDiagnostics {
|
||||
|
||||
@WebMethod(name="heapdump.hprof")
|
||||
public void doHeapDump(StaplerRequest req, StaplerResponse rsp) throws IOException, InterruptedException {
|
||||
owner.checkPermission(Jenkins.RUN_SCRIPTS);
|
||||
owner.checkPermission(Jenkins.ADMINISTER);
|
||||
rsp.setContentType("application/octet-stream");
|
||||
|
||||
FilePath dump = obtain();
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BiPredicate;
|
||||
@@ -144,7 +145,7 @@ public class Retrier <V>{
|
||||
* @return this builder
|
||||
*/
|
||||
public @Nonnull Builder<V> withDuringActionExceptions(@CheckForNull Class<?>[] exceptions) {
|
||||
this.duringActionExceptions = exceptions;
|
||||
this.duringActionExceptions = exceptions != null ? Arrays.copyOf(exceptions, exceptions.length) : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@ import com.google.common.collect.Lists;
|
||||
import jenkins.util.SystemProperties;
|
||||
import hudson.init.InitMilestone;
|
||||
import hudson.init.InitReactorListener;
|
||||
import hudson.security.ACL;
|
||||
import hudson.util.DaemonThreadFactory;
|
||||
import hudson.util.NamingThreadFactory;
|
||||
import jenkins.model.Configuration;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.security.ImpersonatingExecutorService;
|
||||
import org.jvnet.hudson.reactor.Milestone;
|
||||
import org.jvnet.hudson.reactor.Reactor;
|
||||
import org.jvnet.hudson.reactor.ReactorException;
|
||||
@@ -45,7 +47,7 @@ public class InitReactorRunner {
|
||||
else
|
||||
es = Executors.newSingleThreadExecutor(new NamingThreadFactory(new DaemonThreadFactory(), "InitReactorRunner"));
|
||||
try {
|
||||
reactor.execute(es,buildReactorListener());
|
||||
reactor.execute(new ImpersonatingExecutorService(es, ACL.SYSTEM), buildReactorListener());
|
||||
} finally {
|
||||
es.shutdownNow(); // upon a successful return the executor queue should be empty. Upon an exception, we want to cancel all pending tasks
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import hudson.security.csrf.GlobalCrumbIssuerConfiguration;
|
||||
import jenkins.model.JenkinsLocationConfiguration;
|
||||
import jenkins.security.seed.UserSeedProperty;
|
||||
import jenkins.util.SystemProperties;
|
||||
@@ -133,7 +134,7 @@ public class SetupWizard extends PageDecorator {
|
||||
jenkins.setSlaveAgentPort(SystemProperties.getInteger(Jenkins.class.getName()+".slaveAgentPort",-1));
|
||||
|
||||
// require a crumb issuer
|
||||
jenkins.setCrumbIssuer(new DefaultCrumbIssuer(SystemProperties.getBoolean(Jenkins.class.getName() + ".crumbIssuerProxyCompatibility",false)));
|
||||
jenkins.setCrumbIssuer(GlobalCrumbIssuerConfiguration.createDefaultCrumbIssuer());
|
||||
|
||||
// set master -> slave security:
|
||||
jenkins.getInjector().getInstance(AdminWhitelistRule.class)
|
||||
|
||||
@@ -26,8 +26,12 @@ package jenkins.management;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.model.ManagementLink;
|
||||
import hudson.security.Permission;
|
||||
import jenkins.model.Jenkins;
|
||||
import org.jenkinsci.Symbol;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
|
||||
*/
|
||||
@@ -48,6 +52,12 @@ public class CliLink extends ManagementLink {
|
||||
return Messages.CliLink_Description();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Permission getRequiredPermission() {
|
||||
return Jenkins.MANAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return "cli";
|
||||
|
||||
@@ -26,8 +26,12 @@ package jenkins.management;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.model.ManagementLink;
|
||||
import hudson.security.Permission;
|
||||
import jenkins.model.Jenkins;
|
||||
import org.jenkinsci.Symbol;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
|
||||
*/
|
||||
@@ -48,6 +52,12 @@ public class ConfigureLink extends ManagementLink {
|
||||
return Messages.ConfigureLink_Description();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Permission getRequiredPermission() {
|
||||
return Jenkins.READ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return "configure";
|
||||
|
||||
@@ -57,6 +57,6 @@ public class ConsoleLink extends ManagementLink {
|
||||
|
||||
@Override
|
||||
public Permission getRequiredPermission() {
|
||||
return Jenkins.RUN_SCRIPTS;
|
||||
return Jenkins.ADMINISTER;
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,13 @@ package jenkins.management;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.model.ManagementLink;
|
||||
import hudson.security.Permission;
|
||||
import jenkins.management.Messages;
|
||||
import jenkins.model.Jenkins;
|
||||
import org.jenkinsci.Symbol;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
|
||||
*/
|
||||
@@ -49,6 +53,12 @@ public class NodesLink extends ManagementLink {
|
||||
return Messages.NodesLink_Description();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Permission getRequiredPermission() {
|
||||
return Jenkins.MANAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return "computer";
|
||||
|
||||
@@ -26,8 +26,12 @@ package jenkins.management;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.model.ManagementLink;
|
||||
import hudson.security.Permission;
|
||||
import jenkins.model.Jenkins;
|
||||
import org.jenkinsci.Symbol;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
|
||||
*/
|
||||
@@ -48,6 +52,12 @@ public class StatisticsLink extends ManagementLink {
|
||||
return Messages.StatisticsLink_Description();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Permission getRequiredPermission() {
|
||||
return Jenkins.MANAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return "load-statistics";
|
||||
|
||||
@@ -26,8 +26,13 @@ package jenkins.management;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.model.ManagementLink;
|
||||
import hudson.security.Permission;
|
||||
import javax.annotation.Nonnull;
|
||||
import jenkins.model.Jenkins;
|
||||
import org.jenkinsci.Symbol;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
|
||||
*/
|
||||
@@ -48,6 +53,12 @@ public class SystemInfoLink extends ManagementLink {
|
||||
return Messages.SystemInfoLink_Description();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Permission getRequiredPermission() {
|
||||
return Jenkins.SYSTEM_READ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return "systemInfo";
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2019 Daniel Beck
|
||||
*
|
||||
* 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.model;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.model.AsyncPeriodicWork;
|
||||
import hudson.model.Job;
|
||||
import hudson.model.TaskListener;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Background task actually running background build discarders.
|
||||
*
|
||||
* @see GlobalBuildDiscarderConfiguration
|
||||
* @see GlobalBuildDiscarderStrategy
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
@Extension
|
||||
public class BackgroundGlobalBuildDiscarder extends AsyncPeriodicWork {
|
||||
private static final Logger LOGGER = Logger.getLogger(BackgroundGlobalBuildDiscarder.class.getName());
|
||||
|
||||
public BackgroundGlobalBuildDiscarder() {
|
||||
super("Periodic background build discarder"); // TODO i18n
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(TaskListener listener) throws IOException, InterruptedException {
|
||||
for (Job job : Jenkins.get().allItems(Job.class)) {
|
||||
processJob(listener, job);
|
||||
}
|
||||
}
|
||||
|
||||
public static void processJob(TaskListener listener, Job job) {
|
||||
listener.getLogger().println("Processing " + job.getFullName());
|
||||
GlobalBuildDiscarderConfiguration.get().getConfiguredBuildDiscarders().forEach(strategy -> {
|
||||
String displayName = strategy.getDescriptor().getDisplayName();
|
||||
listener.getLogger().println("Offering " + job.getFullName() + " to " + displayName);
|
||||
if (strategy.isApplicable(job)) {
|
||||
listener.getLogger().println(job.getFullName() + " accepted by " + displayName);
|
||||
try {
|
||||
strategy.apply(job);
|
||||
} catch (Exception ex) {
|
||||
listener.error("An exception occurred when executing " + displayName + ": " + ex.getMessage());
|
||||
LOGGER.log(Level.WARNING, "An exception occurred when executing " + displayName, ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRecurrencePeriod() {
|
||||
return HOUR;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2019 Daniel Beck
|
||||
*
|
||||
* 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.model;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.ExtensionList;
|
||||
import hudson.util.DescribableList;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.jenkinsci.Symbol;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Global configuration UI for background build discarders
|
||||
*
|
||||
* @see GlobalBuildDiscarderStrategy
|
||||
* @see BackgroundGlobalBuildDiscarder
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
@Extension @Symbol("buildDiscarders")
|
||||
public class GlobalBuildDiscarderConfiguration extends GlobalConfiguration {
|
||||
public static GlobalBuildDiscarderConfiguration get() {
|
||||
return ExtensionList.lookupSingleton(GlobalBuildDiscarderConfiguration.class);
|
||||
}
|
||||
|
||||
private final DescribableList<GlobalBuildDiscarderStrategy, GlobalBuildDiscarderStrategyDescriptor> configuredBuildDiscarders =
|
||||
new DescribableList<>(this, Collections.singletonList(new JobGlobalBuildDiscarderStrategy()));
|
||||
|
||||
private Object readResolve() {
|
||||
configuredBuildDiscarders.setOwner(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DescribableList<GlobalBuildDiscarderStrategy, GlobalBuildDiscarderStrategyDescriptor> getConfiguredBuildDiscarders() {
|
||||
return configuredBuildDiscarders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
|
||||
try {
|
||||
configuredBuildDiscarders.rebuildHetero(req, json, GlobalBuildDiscarderStrategyDescriptor.all(), "configuredBuildDiscarders");
|
||||
return true;
|
||||
} catch (IOException x) {
|
||||
throw new FormException(x, "configuredBuildDiscarders");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2019 Daniel Beck
|
||||
*
|
||||
* 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.model;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.model.Job;
|
||||
import hudson.model.Run;
|
||||
import hudson.model.listeners.RunListener;
|
||||
import hudson.util.LogTaskListener;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Run background build discarders on an individual job once a build is finalized
|
||||
*/
|
||||
@Extension
|
||||
@Restricted(NoExternalUse.class)
|
||||
public class GlobalBuildDiscarderListener extends RunListener<Run> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(GlobalBuildDiscarderListener.class.getName());
|
||||
|
||||
@Override
|
||||
public void onFinalized(Run run) {
|
||||
Job job = run.getParent();
|
||||
BackgroundGlobalBuildDiscarder.processJob(new LogTaskListener(LOGGER, Level.FINE), job);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2019 Daniel Beck
|
||||
*
|
||||
* 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.model;
|
||||
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.model.AbstractDescribableImpl;
|
||||
import hudson.model.Job;
|
||||
import hudson.model.Run;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Extension point for global background build discarders.
|
||||
*
|
||||
* @see BackgroundGlobalBuildDiscarder
|
||||
* @see GlobalBuildDiscarderConfiguration
|
||||
* @see JobGlobalBuildDiscarderStrategy
|
||||
*/
|
||||
public abstract class GlobalBuildDiscarderStrategy extends AbstractDescribableImpl<GlobalBuildDiscarderStrategy> implements ExtensionPoint {
|
||||
private static final Logger LOGGER = Logger.getLogger(GlobalBuildDiscarderStrategy.class.getName());
|
||||
|
||||
/**
|
||||
* Returns true if and only if this strategy applies to the given job.
|
||||
* @param job
|
||||
* @return true if and only if this strategy applies to the given job.
|
||||
*/
|
||||
public abstract boolean isApplicable(Job<?, ?> job);
|
||||
|
||||
/**
|
||||
* Applies this build discarder strategy to the given job, i.e. delete builds based on this strategy's configuration.
|
||||
*
|
||||
* The default implementation calls {@link #apply(Run)} on each build.
|
||||
*
|
||||
* @param job
|
||||
* @throws IOException
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public void apply(Job<? extends Job, ? extends Run> job) throws IOException, InterruptedException {
|
||||
for (Run<? extends Job, ? extends Run> run : job.getBuilds()) {
|
||||
try {
|
||||
apply(run);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.WARNING, "Failed to delete " + run.getFullDisplayName(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this build discarder strategy to the given run, i.e. delete builds based on this strategy's configuration.
|
||||
*
|
||||
* @param run
|
||||
* @throws IOException
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public void apply(Run<?, ?> run) throws IOException, InterruptedException {
|
||||
// no-op by default
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2019 Daniel Beck
|
||||
*
|
||||
* 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.model;
|
||||
|
||||
import hudson.DescriptorExtensionList;
|
||||
import hudson.model.Descriptor;
|
||||
|
||||
/**
|
||||
* {@link Descriptor} for {@link BackgroundGlobalBuildDiscarder}.
|
||||
*/
|
||||
public abstract class GlobalBuildDiscarderStrategyDescriptor extends Descriptor<GlobalBuildDiscarderStrategy> {
|
||||
|
||||
public static DescriptorExtensionList<GlobalBuildDiscarderStrategy, GlobalBuildDiscarderStrategyDescriptor> all() {
|
||||
return Jenkins.get().getDescriptorList(GlobalBuildDiscarderStrategy.class);
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,15 @@
|
||||
package jenkins.model;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.security.Permission;
|
||||
import jenkins.model.ProjectNamingStrategy.DefaultProjectNamingStrategy;
|
||||
import net.sf.json.JSONObject;
|
||||
|
||||
import org.jenkinsci.Symbol;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Configures the project naming strategy.
|
||||
*
|
||||
@@ -59,4 +62,10 @@ public class GlobalProjectNamingStrategyConfiguration extends GlobalConfiguratio
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Permission getRequiredGlobalConfigPagePermission() {
|
||||
return Jenkins.MANAGE;
|
||||
}
|
||||
}
|
||||
@@ -24,10 +24,12 @@
|
||||
package jenkins.model;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.security.Permission;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.jenkinsci.Symbol;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
@@ -57,4 +59,10 @@ public class GlobalQuietPeriodConfiguration extends GlobalConfiguration {
|
||||
throw new FormException(e,"quietPeriod");
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Permission getRequiredGlobalConfigPagePermission() {
|
||||
return Jenkins.MANAGE;
|
||||
}
|
||||
}
|
||||
@@ -24,11 +24,13 @@
|
||||
package jenkins.model;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.security.Permission;
|
||||
import net.sf.json.JSONException;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.jenkinsci.Symbol;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
@@ -54,4 +56,10 @@ public class GlobalSCMRetryCountConfiguration extends GlobalConfiguration {
|
||||
throw new FormException(e.getMessage(), "quietPeriod");
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Permission getRequiredGlobalConfigPagePermission() {
|
||||
return Jenkins.MANAGE;
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,8 @@ import com.thoughtworks.xstream.XStream;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.*;
|
||||
import hudson.Launcher.LocalLauncher;
|
||||
import hudson.security.csrf.DefaultCrumbIssuer;
|
||||
import hudson.security.csrf.GlobalCrumbIssuerConfiguration;
|
||||
import jenkins.AgentProtocol;
|
||||
import jenkins.diagnostics.URICheckEncodingMonitor;
|
||||
import jenkins.security.stapler.DoActionFilter;
|
||||
@@ -293,6 +295,7 @@ import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
@@ -653,7 +656,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
/**
|
||||
* {@link hudson.security.csrf.CrumbIssuer}
|
||||
*/
|
||||
private volatile CrumbIssuer crumbIssuer;
|
||||
private volatile CrumbIssuer crumbIssuer = GlobalCrumbIssuerConfiguration.createDefaultCrumbIssuer();
|
||||
|
||||
/**
|
||||
* All labels known to Jenkins. This allows us to reuse the same label instances
|
||||
@@ -1762,12 +1765,21 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
*/
|
||||
@Exported(name="jobs")
|
||||
public List<TopLevelItem> getItems() {
|
||||
return getItems(t -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets just the immediate children of {@link Jenkins} based on supplied predicate.
|
||||
*
|
||||
* @see #getAllItems(Class)
|
||||
* @since TODO
|
||||
*/
|
||||
public List<TopLevelItem> getItems(Predicate<TopLevelItem> pred) {
|
||||
List<TopLevelItem> viewableItems = new ArrayList<>();
|
||||
for (TopLevelItem item : items.values()) {
|
||||
if (item.hasPermission(Item.READ))
|
||||
if (pred.test(item) && item.hasPermission(Item.READ))
|
||||
viewableItems.add(item);
|
||||
}
|
||||
|
||||
return viewableItems;
|
||||
}
|
||||
|
||||
@@ -1787,9 +1799,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
*/
|
||||
public <T> List<T> getItems(Class<T> type) {
|
||||
List<T> r = new ArrayList<>();
|
||||
for (TopLevelItem i : getItems())
|
||||
if (type.isInstance(i))
|
||||
r.add(type.cast(i));
|
||||
for (TopLevelItem i : getItems(type::isInstance)) {
|
||||
r.add(type.cast(i));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -2190,6 +2202,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
* @since 2.64
|
||||
*/
|
||||
public List<AdministrativeMonitor> getActiveAdministrativeMonitors() {
|
||||
if (!Jenkins.get().hasPermission(ADMINISTER)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return administrativeMonitors.stream().filter(m -> {
|
||||
try {
|
||||
return m.isEnabled() && m.isActivated();
|
||||
@@ -3285,9 +3300,8 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Initialize the filter with the crumb issuer
|
||||
setCrumbIssuer(crumbIssuer);
|
||||
// Allow the disabling system property to interfere here
|
||||
setCrumbIssuer(getCrumbIssuer());
|
||||
|
||||
// auto register root actions
|
||||
for (Action a : getExtensionList(RootAction.class))
|
||||
@@ -3753,7 +3767,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
public synchronized void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException {
|
||||
BulkChange bc = new BulkChange(this);
|
||||
try {
|
||||
checkPermission(ADMINISTER);
|
||||
checkPermission(MANAGE);
|
||||
|
||||
JSONObject json = req.getSubmittedForm();
|
||||
|
||||
@@ -3781,7 +3795,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
*/
|
||||
@CheckForNull
|
||||
public CrumbIssuer getCrumbIssuer() {
|
||||
return crumbIssuer;
|
||||
return GlobalCrumbIssuerConfiguration.DISABLE_CSRF_PROTECTION ? null : crumbIssuer;
|
||||
}
|
||||
|
||||
public void setCrumbIssuer(CrumbIssuer issuer) {
|
||||
@@ -4449,7 +4463,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
*/
|
||||
public static void _doScript(StaplerRequest req, StaplerResponse rsp, RequestDispatcher view, VirtualChannel channel, ACL acl) throws IOException, ServletException {
|
||||
// ability to run arbitrary script is dangerous
|
||||
acl.checkPermission(RUN_SCRIPTS);
|
||||
acl.checkPermission(ADMINISTER);
|
||||
|
||||
String text = req.getParameter("script");
|
||||
if (text != null) {
|
||||
@@ -4479,7 +4493,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
*/
|
||||
@RequirePOST
|
||||
public void doEval(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
|
||||
checkPermission(RUN_SCRIPTS);
|
||||
checkPermission(ADMINISTER);
|
||||
req.getWebApp().getDispatchValidator().allowDispatch(req, rsp);
|
||||
try {
|
||||
MetaClass mc = req.getWebApp().getMetaClass(getClass());
|
||||
@@ -5235,7 +5249,48 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
||||
|
||||
public static final PermissionGroup PERMISSIONS = Permission.HUDSON_PERMISSIONS;
|
||||
public static final Permission ADMINISTER = Permission.HUDSON_ADMINISTER;
|
||||
|
||||
/**
|
||||
* This permission grants access to parts of the Jenkins system configuration.
|
||||
*
|
||||
* <p>Only features that won't have an impact on Jenkins' overall security and stability should have their
|
||||
* permission requirement lowered from {@link #ADMINISTER} to {@code MANAGE}.
|
||||
* For example, many scripting and code execution features (e.g., configuring master agents, paths to tools on master, etc.)
|
||||
* are unsafe to make available to users with only this permission,
|
||||
* as they could be used to bypass permission enforcement and elevate permissions.</p>
|
||||
*
|
||||
* <p>This permission is disabled by default and support for it considered experimental.
|
||||
* Administrators can set the system property {@code jenkins.security.ManagePermission} to enable it.</p>
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
@Restricted(Beta.class)
|
||||
public static final Permission MANAGE = new Permission(PERMISSIONS, "Manage",
|
||||
Messages._Jenkins_Manage_Description(),
|
||||
ADMINISTER,
|
||||
SystemProperties.getBoolean("jenkins.security.ManagePermission"),
|
||||
new PermissionScope[]{PermissionScope.JENKINS});
|
||||
|
||||
/**
|
||||
* Allows read-only access to large parts of the system configuration.
|
||||
*
|
||||
* When combined with {@link #MANAGE}, it is expected that everything is shown as if only {@link #SYSTEM_READ} was granted,
|
||||
* but that only options editable by users with {@link #MANAGE} are editable.
|
||||
*/
|
||||
@Restricted(Beta.class)
|
||||
public static final Permission SYSTEM_READ = new Permission(PERMISSIONS, "SystemRead",
|
||||
Messages._Jenkins_SystemRead_Description(),
|
||||
ADMINISTER,
|
||||
SystemProperties.getBoolean("jenkins.security.SystemReadPermission"),
|
||||
new PermissionScope[]{PermissionScope.JENKINS});
|
||||
|
||||
@Restricted(NoExternalUse.class) // called by jelly
|
||||
public static final Permission[] MANAGE_AND_SYSTEM_READ =
|
||||
new Permission[] { MANAGE, SYSTEM_READ };
|
||||
|
||||
public static final Permission READ = new Permission(PERMISSIONS,"Read",Messages._Hudson_ReadPermission_Description(),Permission.READ,PermissionScope.JENKINS);
|
||||
/** @deprecated in Jenkins 2.222 use {@link Jenkins#ADMINISTER} instead */
|
||||
@Deprecated
|
||||
public static final Permission RUN_SCRIPTS = new Permission(PERMISSIONS, "RunScripts", Messages._Hudson_RunScriptsPermission_Description(),ADMINISTER,PermissionScope.JENKINS);
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2019 Daniel Beck
|
||||
*
|
||||
* 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.model;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.model.Job;
|
||||
import org.jenkinsci.Symbol;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
import org.kohsuke.stapler.DataBoundConstructor;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Periodically call a job's configured build discarder in the background.
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public class JobGlobalBuildDiscarderStrategy extends GlobalBuildDiscarderStrategy {
|
||||
|
||||
@DataBoundConstructor
|
||||
public JobGlobalBuildDiscarderStrategy() {
|
||||
// required for data binding
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(Job<?, ?> job) {
|
||||
return job.getBuildDiscarder() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Job<?, ?> job) throws IOException, InterruptedException {
|
||||
job.logRotate();
|
||||
}
|
||||
|
||||
@Extension @Symbol("jobBuildDiscarder")
|
||||
public static class DescriptorImpl extends GlobalBuildDiscarderStrategyDescriptor {
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return Messages.JobGlobalBuildDiscarderStrategy_displayName();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
package jenkins.model;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.Extension;
|
||||
import hudson.Util;
|
||||
import hudson.model.Job;
|
||||
@@ -313,13 +314,19 @@ public final class RunIdMigrator {
|
||||
if (args.length != 1) {
|
||||
throw new Exception("pass one parameter, $JENKINS_HOME");
|
||||
}
|
||||
File root = new File(args[0]);
|
||||
File root = constructFile(args[0]);
|
||||
File jobs = new File(root, "jobs");
|
||||
if (!jobs.isDirectory()) {
|
||||
throw new FileNotFoundException("no such $JENKINS_HOME " + root);
|
||||
}
|
||||
new RunIdMigrator().unmigrateJobsDir(jobs);
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "Only invoked from the command line as a standalone utility")
|
||||
private static File constructFile(String arg) {
|
||||
return new File(arg);
|
||||
}
|
||||
|
||||
private void unmigrateJobsDir(File jobs) throws Exception {
|
||||
File[] jobDirs = jobs.listFiles();
|
||||
if (jobDirs == null) {
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2019 Daniel Beck
|
||||
*
|
||||
* 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.model;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.model.Job;
|
||||
import org.jenkinsci.Symbol;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
import org.kohsuke.stapler.DataBoundConstructor;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Apply a user-specified build discarder periodically on all jobs.
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public class SimpleGlobalBuildDiscarderStrategy extends GlobalBuildDiscarderStrategy {
|
||||
|
||||
private BuildDiscarder discarder;
|
||||
|
||||
@DataBoundConstructor
|
||||
public SimpleGlobalBuildDiscarderStrategy(BuildDiscarder discarder) {
|
||||
this.discarder = discarder;
|
||||
}
|
||||
|
||||
public BuildDiscarder getDiscarder() {
|
||||
return discarder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(Job<?, ?> job) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Job<?, ?> job) throws IOException, InterruptedException {
|
||||
if (discarder != null) {
|
||||
discarder.perform(job);
|
||||
}
|
||||
}
|
||||
|
||||
@Extension
|
||||
@Symbol("simpleBuildDiscarder")
|
||||
public static class DescriptorImpl extends GlobalBuildDiscarderStrategyDescriptor {
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return Messages.SimpleGlobalBuildDiscarderStrategy_displayName();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package jenkins.model.identity;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.Extension;
|
||||
import hudson.model.UnprotectedRootAction;
|
||||
import java.security.MessageDigest;
|
||||
@@ -61,6 +62,7 @@ public class IdentityRootAction implements UnprotectedRootAction {
|
||||
*
|
||||
* @return the fingerprint of the public key.
|
||||
*/
|
||||
@SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_MD5", justification = "Not used for security. ")
|
||||
public String getFingerprint() {
|
||||
RSAPublicKey key = InstanceIdentityProvider.RSA.getPublicKey();
|
||||
if (key == null) {
|
||||
@@ -68,7 +70,7 @@ public class IdentityRootAction implements UnprotectedRootAction {
|
||||
}
|
||||
// TODO replace with org.jenkinsci.remoting.util.KeyUtils once JENKINS-36871 changes are merged
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("MD5");
|
||||
MessageDigest digest = getMd5();
|
||||
digest.reset();
|
||||
byte[] bytes = digest.digest(key.getEncoded());
|
||||
StringBuilder result = new StringBuilder(Math.max(0, bytes.length * 3 - 1));
|
||||
@@ -84,4 +86,10 @@ public class IdentityRootAction implements UnprotectedRootAction {
|
||||
throw new IllegalStateException("JLS mandates MD5 support");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO JENKINS-60563 remove MD5 from all usages in Jenkins
|
||||
@SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_MD5", justification = "Not used for security. ")
|
||||
private MessageDigest getMd5() throws NoSuchAlgorithmException {
|
||||
return MessageDigest.getInstance("MD5");
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ public abstract class AbstractLazyLoadRunMap<R> extends AbstractMap<Integer,R> i
|
||||
/**
|
||||
* Used in {@link #all()} to quickly determine if we've already loaded everything.
|
||||
*/
|
||||
private boolean fullyLoaded;
|
||||
private volatile boolean fullyLoaded;
|
||||
|
||||
/**
|
||||
* Currently visible index.
|
||||
|
||||
@@ -212,7 +212,7 @@ public class AdminWhitelistRule implements StaplerProxy {
|
||||
public void setMasterKillSwitch(boolean state) {
|
||||
final Jenkins jenkins = Jenkins.get();
|
||||
try {
|
||||
jenkins.checkPermission(Jenkins.RUN_SCRIPTS);
|
||||
jenkins.checkPermission(Jenkins.ADMINISTER);
|
||||
File f = getMasterKillSwitchFile(jenkins);
|
||||
FileUtils.writeStringToFile(f, Boolean.toString(state), Charset.defaultCharset());
|
||||
// treat the file as the canonical source of information in case write fails
|
||||
@@ -227,7 +227,7 @@ public class AdminWhitelistRule implements StaplerProxy {
|
||||
*/
|
||||
@Override
|
||||
public Object getTarget() {
|
||||
Jenkins.get().checkPermission(Jenkins.RUN_SCRIPTS);
|
||||
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ public class MasterKillSwitchConfiguration extends GlobalConfiguration {
|
||||
* Unless this option is relevant, we don't let users choose this.
|
||||
*/
|
||||
public boolean isRelevant() {
|
||||
return jenkins.hasPermission(Jenkins.RUN_SCRIPTS) && jenkins.isUseSecurity();
|
||||
return jenkins.hasPermission(Jenkins.ADMINISTER) && jenkins.isUseSecurity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class JnlpSlaveAgentProtocol4 extends AgentProtocol {
|
||||
|
||||
// prepare our keyStore so we can provide our authentication
|
||||
keyStore = KeyStore.getInstance("JKS");
|
||||
char[] password = "password".toCharArray();
|
||||
char[] password = constructPassword();
|
||||
try {
|
||||
keyStore.load(null, password);
|
||||
} catch (IOException e) {
|
||||
@@ -146,6 +146,10 @@ public class JnlpSlaveAgentProtocol4 extends AgentProtocol {
|
||||
sslContext.init(kmf.getKeyManagers(), trustManagers, null);
|
||||
}
|
||||
|
||||
private char[] constructPassword() {
|
||||
return "password".toCharArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the {@link IOHubProvider}
|
||||
*
|
||||
@@ -182,7 +186,7 @@ public class JnlpSlaveAgentProtocol4 extends AgentProtocol {
|
||||
LOGGER.log(Level.INFO, "Updating {0} TLS certificate to retain validity", getName());
|
||||
X509Certificate identityCertificate = InstanceIdentityProvider.RSA.getCertificate();
|
||||
RSAPrivateKey privateKey = InstanceIdentityProvider.RSA.getPrivateKey();
|
||||
char[] password = "password".toCharArray();
|
||||
char[] password = constructPassword();
|
||||
keyStore.setKeyEntry("jenkins", privateKey, password, new X509Certificate[]{identityCertificate});
|
||||
}
|
||||
} catch (KeyStoreException e) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
package jenkins.telemetry.impl;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.util.VersionNumber;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.telemetry.Telemetry;
|
||||
import jenkins.util.SystemProperties;
|
||||
@@ -97,7 +98,8 @@ public class SecuritySystemProperties extends Telemetry {
|
||||
putStringInfo(security, "hudson.security.HudsonPrivateSecurityRealm.ID_REGEX");
|
||||
|
||||
Map<String, Object> info = new TreeMap<>();
|
||||
info.put("core", Jenkins.getVersion().toString());
|
||||
VersionNumber jenkinsVersion = Jenkins.getVersion();
|
||||
info.put("core", jenkinsVersion != null ? jenkinsVersion.toString() : "UNKNOWN");
|
||||
info.put("clientDate", clientDateString());
|
||||
info.put("properties", security);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ package jenkins.telemetry.impl.java11;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import hudson.Extension;
|
||||
import hudson.util.VersionNumber;
|
||||
import io.jenkins.lib.versionnumber.JavaSpecificationVersion;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.telemetry.Telemetry;
|
||||
@@ -160,7 +161,8 @@ public class MissingClassTelemetry extends Telemetry {
|
||||
}
|
||||
|
||||
JSONObject info = new JSONObject();
|
||||
info.put("core", Jenkins.getVersion() != null ? Jenkins.getVersion().toString() : "UNKNOWN");
|
||||
VersionNumber jenkinsVersion = Jenkins.getVersion();
|
||||
info.put("core", jenkinsVersion != null ? jenkinsVersion.toString() : "UNKNOWN");
|
||||
info.put("clientDate", clientDateString());
|
||||
info.put("classMissingEvents", events);
|
||||
|
||||
|
||||
@@ -23,14 +23,13 @@
|
||||
*/
|
||||
package jenkins.tools;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import hudson.Extension;
|
||||
import hudson.Functions;
|
||||
import hudson.model.Descriptor;
|
||||
import hudson.model.ManagementLink;
|
||||
import hudson.security.Permission;
|
||||
import hudson.util.FormApply;
|
||||
import jenkins.model.GlobalConfigurationCategory;
|
||||
import java.util.function.Predicate;
|
||||
import jenkins.model.Jenkins;
|
||||
import net.sf.json.JSONObject;
|
||||
|
||||
@@ -81,12 +80,12 @@ public class GlobalToolConfiguration extends ManagementLink {
|
||||
FormApply.success(req.getContextPath() + "/manage").generateResponse(req, rsp, null);
|
||||
}
|
||||
|
||||
private boolean configure(StaplerRequest req, JSONObject json) throws hudson.model.Descriptor.FormException, IOException {
|
||||
private boolean configure(StaplerRequest req, JSONObject json) throws Descriptor.FormException, IOException {
|
||||
Jenkins j = Jenkins.get();
|
||||
j.checkPermission(Jenkins.ADMINISTER);
|
||||
|
||||
boolean result = true;
|
||||
for(Descriptor<?> d : Functions.getSortedDescriptorsForGlobalConfig(FILTER)){
|
||||
for (Descriptor<?> d : Functions.getSortedDescriptorsForGlobalConfigByDescriptor(FILTER)) {
|
||||
result &= configureDescriptor(req, json, d);
|
||||
}
|
||||
j.save();
|
||||
@@ -101,11 +100,7 @@ public class GlobalToolConfiguration extends ManagementLink {
|
||||
return d.configure(req, js);
|
||||
}
|
||||
|
||||
public static Predicate<GlobalConfigurationCategory> FILTER = new Predicate<GlobalConfigurationCategory>() {
|
||||
public boolean apply(GlobalConfigurationCategory input) {
|
||||
return input instanceof ToolConfigurationCategory;
|
||||
}
|
||||
};
|
||||
public static Predicate<Descriptor> FILTER = input -> input.getCategory() instanceof ToolConfigurationCategory;
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(GlobalToolConfiguration.class.getName());
|
||||
}
|
||||
Reference in new issue
Block a user