diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 01a8d3cc0ed391d8350901de80894214524c36a0..6b5cf97304631eb606e8b29dfd0a14301504c9eb 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -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); } diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 50339569b63952fb02f28ddeadafa2c8137a053b..62208aaf93dd35344527be625516e797eb9017b6 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -1062,6 +1062,10 @@ public class Functions { Descriptor d = c.getInstance(); if (d.getGlobalConfigPage()==null) continue; + if (!Jenkins.get().hasPermission(d.getPermission())) { + continue; + } + if (predicate.apply(d.getCategory())) { r.add(new Tag(c.ordinal(), d)); } diff --git a/core/src/main/java/hudson/model/Descriptor.java b/core/src/main/java/hudson/model/Descriptor.java index 463cdee36628986a5f7a7266b5d3f59ecf151cc0..3a055d8119f55cf023c5d4afdabd5468fb82eacf 100644 --- a/core/src/main/java/hudson/model/Descriptor.java +++ b/core/src/main/java/hudson/model/Descriptor.java @@ -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,18 @@ public abstract class Descriptor> implements Saveable, return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Unclassified.class); } + /** + * Returns the permission type needed in order to configure the descriptor if and only if it is configured through the global (Unclassified) configuration. + * By default, requires {@link Jenkins.ADMINISTER} permission. + * Override to return something different if appropriate. The only currently supported alternative return value is {@link Jenkins.MANAGE}. + * + * @return Permission required to configure this descriptor. + */ + public @Nonnull + Permission getPermission() { + return Jenkins.ADMINISTER; + } + private String getViewPage(Class clazz, String pageName, String defaultValue) { return getViewPage(clazz,Collections.singleton(pageName),defaultValue); } diff --git a/core/src/main/java/hudson/model/ManageJenkinsAction.java b/core/src/main/java/hudson/model/ManageJenkinsAction.java index b1fb854a10c1f779b4511f19e02b3f1c569bab97..06659b19d8ae6e490b6881f10b6f40235ca9f92a 100644 --- a/core/src/main/java/hudson/model/ManageJenkinsAction.java +++ b/core/src/main/java/hudson/model/ManageJenkinsAction.java @@ -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().hasPermission(Jenkins.MANAGE)) return "gear2.png"; else return null; diff --git a/core/src/main/java/hudson/model/ManagementLink.java b/core/src/main/java/hudson/model/ManagementLink.java index 00d6871097cc66730ad6a50caa3bb3d3bf15b67a..05db6030f029e9d30c90d2ede9dd0227ae23990a 100644 --- a/core/src/main/java/hudson/model/ManagementLink.java +++ b/core/src/main/java/hudson/model/ManagementLink.java @@ -115,7 +115,7 @@ public abstract class ManagementLink implements ExtensionPoint, Action { * @return permission required for user to access this management link, in addition to {@link Jenkins#ADMINISTER} */ public @CheckForNull Permission getRequiredPermission() { - return null; + return Jenkins.ADMINISTER; } /** diff --git a/core/src/main/java/jenkins/management/CliLink.java b/core/src/main/java/jenkins/management/CliLink.java index 718504727fc027aba1453dbe24926f338a3644dc..6a2c67f7919e9d1aa34787863b02b9d223ddf24d 100644 --- a/core/src/main/java/jenkins/management/CliLink.java +++ b/core/src/main/java/jenkins/management/CliLink.java @@ -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.CheckForNull; + /** * @author Nicolas De Loof */ @@ -48,6 +52,12 @@ public class CliLink extends ManagementLink { return Messages.CliLink_Description(); } + @CheckForNull + @Override + public Permission getRequiredPermission() { + return Jenkins.MANAGE; + } + @Override public String getUrlName() { return "cli"; diff --git a/core/src/main/java/jenkins/management/ConfigureLink.java b/core/src/main/java/jenkins/management/ConfigureLink.java index a10df71b2289b05b13c9486bc44d6cf526591f6f..61fe8f1103e1ccffa67529008c493f7d2942b9d6 100644 --- a/core/src/main/java/jenkins/management/ConfigureLink.java +++ b/core/src/main/java/jenkins/management/ConfigureLink.java @@ -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.CheckForNull; + /** * @author Nicolas De Loof */ @@ -48,6 +52,12 @@ public class ConfigureLink extends ManagementLink { return Messages.ConfigureLink_Description(); } + @CheckForNull + @Override + public Permission getRequiredPermission() { + return Jenkins.MANAGE; + } + @Override public String getUrlName() { return "configure"; diff --git a/core/src/main/java/jenkins/management/NodesLink.java b/core/src/main/java/jenkins/management/NodesLink.java index 90038450cebccf98c3a0bc900f7faf0894cd1b3b..3f85b701b98ce8d2c35c7332cb71eaa7de25641e 100644 --- a/core/src/main/java/jenkins/management/NodesLink.java +++ b/core/src/main/java/jenkins/management/NodesLink.java @@ -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.CheckForNull; + /** * @author Nicolas De Loof */ @@ -49,6 +53,12 @@ public class NodesLink extends ManagementLink { return Messages.NodesLink_Description(); } + @CheckForNull + @Override + public Permission getRequiredPermission() { + return Jenkins.MANAGE; + } + @Override public String getUrlName() { return "computer"; diff --git a/core/src/main/java/jenkins/management/StatisticsLink.java b/core/src/main/java/jenkins/management/StatisticsLink.java index 9821781d789aea6c19380fc2465e2d58ee32efc3..83ff09edd3a992531f0dc9a37d55a8c275e4ef0b 100644 --- a/core/src/main/java/jenkins/management/StatisticsLink.java +++ b/core/src/main/java/jenkins/management/StatisticsLink.java @@ -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.CheckForNull; + /** * @author Nicolas De Loof */ @@ -48,6 +52,12 @@ public class StatisticsLink extends ManagementLink { return Messages.StatisticsLink_Description(); } + @CheckForNull + @Override + public Permission getRequiredPermission() { + return Jenkins.MANAGE; + } + @Override public String getUrlName() { return "load-statistics"; diff --git a/core/src/main/java/jenkins/model/GlobalProjectNamingStrategyConfiguration.java b/core/src/main/java/jenkins/model/GlobalProjectNamingStrategyConfiguration.java index 2502df1b6e1a98b99945ea20646176cd6666e488..4f046291f2448d5adb8b2a9532afbf055854cf8a 100644 --- a/core/src/main/java/jenkins/model/GlobalProjectNamingStrategyConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalProjectNamingStrategyConfiguration.java @@ -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 getPermission() { + return Jenkins.MANAGE; + } } diff --git a/core/src/main/java/jenkins/model/GlobalQuietPeriodConfiguration.java b/core/src/main/java/jenkins/model/GlobalQuietPeriodConfiguration.java index b0cdd84d3d3dd9916eaa89713ced86300312e90c..93495cd3848a3b5eae00d0d731ee30548f141f20 100644 --- a/core/src/main/java/jenkins/model/GlobalQuietPeriodConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalQuietPeriodConfiguration.java @@ -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 getPermission() { + return Jenkins.MANAGE; + } } diff --git a/core/src/main/java/jenkins/model/GlobalSCMRetryCountConfiguration.java b/core/src/main/java/jenkins/model/GlobalSCMRetryCountConfiguration.java index 32d0c155c600f7bf3129c2e15be282bdfc7908a4..373eb62828ce100236a0d3b7db532af3564e7709 100644 --- a/core/src/main/java/jenkins/model/GlobalSCMRetryCountConfiguration.java +++ b/core/src/main/java/jenkins/model/GlobalSCMRetryCountConfiguration.java @@ -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 getPermission() { + return Jenkins.MANAGE; + } } diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 065251d881f4d9bccb91cf5cfcca8ce598d32c0c..9a44b86e6f12ae73060e95f75f277a2d833fbe64 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -2199,6 +2199,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve * @since 2.64 */ public List getActiveAdministrativeMonitors() { + if (!Jenkins.get().hasPermission(ADMINISTER)) { + return Collections.emptyList(); + } return administrativeMonitors.stream().filter(m -> { try { return m.isEnabled() && m.isActivated(); @@ -3762,7 +3765,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(); @@ -5236,7 +5239,25 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve private static final Logger LOGGER = Logger.getLogger(Jenkins.class.getName()); public static final PermissionGroup PERMISSIONS = Permission.HUDSON_PERMISSIONS; + /** + * Grants ability to configure any and all aspects of the Jenkins instance + */ public static final Permission ADMINISTER = Permission.HUDSON_ADMINISTER; + + /** + * Allows non-privilege escalating configuration permission for a Jenkins instance. Actions which could result + * in a privilege escalation (such as RUN_SCRIPTS) require explicit ADMINISTER permission. + * + * As an experimental feature, making the manage permission able to be disabled by default (keep as ADMINISTER), can + * be enabled with "jenkins.security.ManagePermission" system property. + */ + public static final Permission MANAGE = new Permission(PERMISSIONS, "Manage", + Messages._Jenkins_Manage_Description(), + ADMINISTER, + SystemProperties.getBoolean("jenkins.security.ManagePermission"), + new PermissionScope[]{PermissionScope.JENKINS}); + + public static final Permission READ = new Permission(PERMISSIONS,"Read",Messages._Hudson_ReadPermission_Description(),Permission.READ,PermissionScope.JENKINS); public static final Permission RUN_SCRIPTS = new Permission(PERMISSIONS, "RunScripts", Messages._Hudson_RunScriptsPermission_Description(),ADMINISTER,PermissionScope.JENKINS); diff --git a/core/src/main/resources/hudson/PluginManager/sidepanel.groovy b/core/src/main/resources/hudson/PluginManager/sidepanel.groovy index 375fda352d0b4b2c56e2cb4eec8f8250522dd775..a3aa54878db362428991de26e625bc0557f2d8ff 100644 --- a/core/src/main/resources/hudson/PluginManager/sidepanel.groovy +++ b/core/src/main/resources/hudson/PluginManager/sidepanel.groovy @@ -28,7 +28,7 @@ l.header() l.side_panel { l.tasks { l.task(icon:"icon-up icon-md", href:rootURL+'/', title:_("Back to Dashboard")) - l.task(icon:"icon-gear2 icon-md", href:"${rootURL}/manage", title:_("Manage Jenkins"), permission:app.ADMINISTER, it:app) + l.task(icon:"icon-gear2 icon-md", href:"${rootURL}/manage", title:_("Manage Jenkins"), permission:app.MANAGE, it:app) if (!app.updateCenter.jobs.isEmpty()) { l.task(icon:"icon-plugin icon-md", href:"${rootURL}/updateCenter/", title:_("Update Center")) } diff --git a/core/src/main/resources/hudson/model/ComputerSet/sidepanel.jelly b/core/src/main/resources/hudson/model/ComputerSet/sidepanel.jelly index 1a9a25722591a7b4842fa5b95564c3a27de0cb99..919e67e648de2b95290f1208297332a110207bc0 100644 --- a/core/src/main/resources/hudson/model/ComputerSet/sidepanel.jelly +++ b/core/src/main/resources/hudson/model/ComputerSet/sidepanel.jelly @@ -32,7 +32,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/hudson/model/Messages.properties b/core/src/main/resources/hudson/model/Messages.properties index 8807077497623ee2fd62181c7f59b2094c277b9d..5e1553e12a10a71a217d266e605cc924ffde63b3 100644 --- a/core/src/main/resources/hudson/model/Messages.properties +++ b/core/src/main/resources/hudson/model/Messages.properties @@ -163,6 +163,10 @@ Hudson.AdministerPermission.Description=\ This permission grants the ability to make system-wide configuration changes, \ as well as perform highly sensitive operations that amounts to full local system access \ (within the scope granted by the underlying OS.) +Jenkins.Manage.Description=\ + This permission grants the ability to make non Permission escalating system \ + configuration changes. \ + System configuration which would allow the execution of arbitrary commands or code on the master requires Administer. Hudson.ReadPermission.Description=\ The read permission is necessary for viewing almost all pages of Jenkins. \ This permission is useful when you don\u2019t want unauthenticated users to see \ diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/sidepanel.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/sidepanel.jelly index 8d864d01e9d039652025073070074cfdbd2c4546..6cc9f4456925a6c37cf58ff93097e8c7e479d847 100644 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/sidepanel.jelly +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/sidepanel.jelly @@ -28,7 +28,7 @@ THE SOFTWARE. - + diff --git a/core/src/main/resources/jenkins/model/Jenkins/configure.jelly b/core/src/main/resources/jenkins/model/Jenkins/configure.jelly index a85ab58ade6291cf3fa32cb6c15fd551eca9afa3..6c6cfe895ef2d45c5b2579b55a476719b2f64755 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/configure.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/configure.jelly @@ -27,7 +27,7 @@ THE SOFTWARE. --> - + diff --git a/core/src/main/resources/jenkins/model/Jenkins/manage.jelly b/core/src/main/resources/jenkins/model/Jenkins/manage.jelly index d9dd6de40db65523ecd21a4f6b09d9a6f669a26d..16a54119f607ded9c7228404583d26ea7cb1a591 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/manage.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/manage.jelly @@ -27,7 +27,7 @@ THE SOFTWARE. --> - +