diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 7ffbbf40d9d5d416f9d07b0128b488c5bd9591c2..5f8459a255b6c70d078fed23eb01e93f5774874f 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -88,6 +88,7 @@ import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.HttpResponses; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerOverridable; +import org.kohsuke.stapler.StaplerProxy; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.export.Exported; @@ -176,7 +177,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; * @author Kohsuke Kawaguchi */ @ExportedBean -public abstract class PluginManager extends AbstractModelObject implements OnMaster, StaplerOverridable { +public abstract class PluginManager extends AbstractModelObject implements OnMaster, StaplerOverridable, StaplerProxy { /** Custom plugin manager system property or context param. */ public static final String CUSTOM_PLUGIN_MANAGER = PluginManager.class.getName() + ".className"; @@ -2063,4 +2064,19 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } } + + @Override + @Restricted(NoExternalUse.class) + public Object getTarget() { + if (!SKIP_PERMISSION_CHECK) { + Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); + } + return this; + } + + /** + * Escape hatch for StaplerProxy-based access control + */ + @Restricted(NoExternalUse.class) + public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(PluginManager.class.getName() + ".skipPermissionCheck"); } diff --git a/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java b/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java index 14bbb60584bca38a207ca23e8d52c5c8f4a72874..b218d5964077bb45df6909a644e0892276561b18 100644 --- a/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java +++ b/core/src/main/java/hudson/lifecycle/WindowsInstallerLink.java @@ -112,6 +112,8 @@ public class WindowsInstallerLink extends ManagementLink { */ @RequirePOST public void doDoInstall(StaplerRequest req, StaplerResponse rsp, @QueryParameter("dir") String _dir) throws IOException, ServletException { + Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); + if(installationDir!=null) { // installation already complete sendError("Installation is already complete",req,rsp); @@ -121,8 +123,6 @@ public class WindowsInstallerLink extends ManagementLink { sendError(".NET Framework 2.0 or later is required for this feature",req,rsp); return; } - - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); File dir = new File(_dir).getAbsoluteFile(); dir.mkdirs(); @@ -174,6 +174,8 @@ public class WindowsInstallerLink extends ManagementLink { @RequirePOST public void doRestart(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException { + Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); + if(installationDir==null) { // if the user reloads the page after Hudson has restarted, // it comes back here. In such a case, don't let this restart Hudson. @@ -181,7 +183,6 @@ public class WindowsInstallerLink extends ManagementLink { rsp.sendRedirect(req.getContextPath()+"/"); return; } - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); rsp.forward(this,"_restart",req); final File oldRoot = Jenkins.getInstance().getRootDir(); diff --git a/core/src/main/java/hudson/logging/LogRecorderManager.java b/core/src/main/java/hudson/logging/LogRecorderManager.java index bac8c8b6859a644119397b7b4fc8b2004d53a4d9..765253341bc13b4d9fb501320ddf32bb7db82e20 100644 --- a/core/src/main/java/hudson/logging/LogRecorderManager.java +++ b/core/src/main/java/hudson/logging/LogRecorderManager.java @@ -25,6 +25,7 @@ package hudson.logging; import hudson.FeedAdapter; import hudson.Functions; +import hudson.PluginManager; import hudson.init.Initializer; import static hudson.init.InitMilestone.PLUGINS_PREPARED; import hudson.model.AbstractModelObject; @@ -35,7 +36,10 @@ import jenkins.model.JenkinsLocationConfiguration; import jenkins.model.ModelObjectWithChildren; import jenkins.model.ModelObjectWithContextMenu.ContextMenu; import org.apache.commons.io.filefilter.WildcardFileFilter; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerProxy; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.HttpResponse; @@ -61,7 +65,7 @@ import java.util.logging.Logger; * * @author Kohsuke Kawaguchi */ -public class LogRecorderManager extends AbstractModelObject implements ModelObjectWithChildren { +public class LogRecorderManager extends AbstractModelObject implements ModelObjectWithChildren, StaplerProxy { /** * {@link LogRecorder}s keyed by their {@linkplain LogRecorder#name name}. */ @@ -198,4 +202,19 @@ public class LogRecorderManager extends AbstractModelObject implements ModelObje public static void init(Jenkins h) throws IOException { h.getLog().load(); } + + @Override + @Restricted(NoExternalUse.class) + public Object getTarget() { + if (!SKIP_PERMISSION_CHECK) { + Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); + } + return this; + } + + /** + * Escape hatch for StaplerProxy-based access control + */ + @Restricted(NoExternalUse.class) + public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(LogRecorderManager.class.getName() + ".skipPermissionCheck"); } diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java index e06a3f1988c0b0de87d857c545c7662438641ae5..b4f1c9a3324f48fad1fd972f362d03a572e2371e 100644 --- a/core/src/main/java/hudson/model/AbstractItem.java +++ b/core/src/main/java/hudson/model/AbstractItem.java @@ -56,6 +56,7 @@ import jenkins.util.xml.XMLUtils; import org.apache.tools.ant.taskdefs.Copy; import org.apache.tools.ant.types.FileSet; +import org.kohsuke.stapler.StaplerProxy; import org.kohsuke.stapler.WebMethod; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; @@ -103,7 +104,7 @@ import org.kohsuke.stapler.Ancestor; // Item doesn't necessarily have to be Actionable, but // Java doesn't let multiple inheritance. @ExportedBean -public abstract class AbstractItem extends Actionable implements Item, HttpDeletable, AccessControlled, DescriptorByNameOwner { +public abstract class AbstractItem extends Actionable implements Item, HttpDeletable, AccessControlled, DescriptorByNameOwner, StaplerProxy { private static final Logger LOGGER = Logger.getLogger(AbstractItem.class.getName()); @@ -819,6 +820,21 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet return super.toString() + '[' + (parent != null ? getFullName() : "?/" + name) + ']'; } + @Override + @Restricted(NoExternalUse.class) + public Object getTarget() { + if (!SKIP_PERMISSION_CHECK) { + getACL().checkPermission(Item.READ); + } + return this; + } + + /** + * Escape hatch for StaplerProxy-based access control + */ + @Restricted(NoExternalUse.class) + public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(AbstractItem.class.getName() + ".skipPermissionCheck"); + /** * Used for CLI binding. */ diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index a16b96e8f571817727e280c34035ae0d9a31fd65..68a1e9e03bc7eaadefe04fa133d1c0b060d1f4f2 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -324,6 +324,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces * Used to URL-bind {@link AnnotatedLargeText}. */ public AnnotatedLargeText getLogText() { + checkPermission(CONNECT); return new AnnotatedLargeText(getLogFile(), Charset.defaultCharset(), false, this); } diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java index 109efb9d3757c878cbc1a49234efa3dd79acfd9d..097b1d9b2eb5ec34bc331a21c27b53a9fe2753c6 100644 --- a/core/src/main/java/hudson/model/UpdateCenter.java +++ b/core/src/main/java/hudson/model/UpdateCenter.java @@ -70,6 +70,7 @@ import org.jenkinsci.Symbol; import org.jvnet.localizer.Localizable; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.StaplerProxy; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -146,7 +147,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST; * @since 1.220 */ @ExportedBean -public class UpdateCenter extends AbstractModelObject implements Saveable, OnMaster { +public class UpdateCenter extends AbstractModelObject implements Saveable, OnMaster, StaplerProxy { private static final String UPDATE_CENTER_URL = SystemProperties.getString(UpdateCenter.class.getName()+".updateCenterUrl","https://updates.jenkins.io/"); @@ -292,7 +293,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas } public Api getApi() { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); return new Api(this); } @@ -364,7 +364,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas */ @Restricted(DoNotUse.class) public HttpResponse doConnectionStatus(StaplerRequest request) { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); try { String siteId = request.getParameter("siteId"); if (siteId == null) { @@ -417,7 +416,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas */ @Restricted(DoNotUse.class) // WebOnly public HttpResponse doIncompleteInstallStatus() { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); try { Map jobs = InstallUtil.getPersistedInstallStatus(); if(jobs == null) { @@ -467,7 +465,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas */ @Restricted(DoNotUse.class) public HttpResponse doInstallStatus(StaplerRequest request) { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); try { String correlationId = request.getParameter("correlationId"); Map response = new HashMap<>(); @@ -620,7 +617,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas */ @RequirePOST public void doUpgrade(StaplerResponse rsp) throws IOException, ServletException { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); HudsonUpgradeJob job = new HudsonUpgradeJob(getCoreSource(), Jenkins.getAuthentication()); if(!Lifecycle.get().canRewriteHudsonWar()) { sendError("Jenkins upgrade not supported in this running mode"); @@ -639,7 +635,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas */ @RequirePOST public HttpResponse doInvalidateData() { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); for (UpdateSite site : sites) { site.doInvalidateData(); } @@ -655,7 +650,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas public void doSafeRestart(StaplerRequest request, StaplerResponse response) throws IOException, ServletException { synchronized (jobs) { if (!isRestartScheduled()) { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); addJob(new RestartJenkinsJob(getCoreSource())); LOGGER.info("Scheduling Jenkins reboot"); } @@ -726,7 +720,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas */ @RequirePOST public void doDowngrade(StaplerResponse rsp) throws IOException, ServletException { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); if(!isDowngradable()) { sendError("Jenkins downgrade is not possible, probably backup does not exist"); return; @@ -743,7 +736,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas */ @RequirePOST public void doRestart(StaplerResponse rsp) throws IOException, ServletException { - Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); HudsonDowngradeJob job = new HudsonDowngradeJob(getCoreSource(), Jenkins.getAuthentication()); LOGGER.info("Scheduling the core downgrade"); @@ -982,7 +974,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas return results; } - /** * {@link AdministrativeMonitor} that checks if there's Jenkins update. */ @@ -2198,6 +2189,22 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas } } + @Override + @Restricted(NoExternalUse.class) + public Object getTarget() { + if (!SKIP_PERMISSION_CHECK) { + Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); + } + return this; + } + + /** + * Escape hatch for StaplerProxy-based access control + */ + @Restricted(NoExternalUse.class) + public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(UpdateCenter.class.getName() + ".skipPermissionCheck"); + + /** * Sequence number generator. */ diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java index 405f8fe135f6e1131bd051a5e946bc5e183655e0..25483f689a96feb28d572d193deec92ddd20adc6 100644 --- a/core/src/main/java/hudson/model/User.java +++ b/core/src/main/java/hudson/model/User.java @@ -97,6 +97,7 @@ import org.apache.commons.lang.StringUtils; import org.jenkinsci.Symbol; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.StaplerProxy; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.export.Exported; @@ -129,7 +130,7 @@ import org.springframework.dao.DataAccessException; * @author Kohsuke Kawaguchi */ @ExportedBean -public class User extends AbstractModelObject implements AccessControlled, DescriptorByNameOwner, Saveable, Comparable, ModelObjectWithContextMenu { +public class User extends AbstractModelObject implements AccessControlled, DescriptorByNameOwner, Saveable, Comparable, ModelObjectWithContextMenu, StaplerProxy { /** * The username of the 'unknown' user used to avoid null user references. @@ -1099,6 +1100,22 @@ public class User extends AbstractModelObject implements AccessControlled, Descr public ContextMenu doContextMenu(StaplerRequest request, StaplerResponse response) throws Exception { return new ContextMenu().from(this,request,response); } + + @Override + @Restricted(NoExternalUse.class) + public Object getTarget() { + if (!SKIP_PERMISSION_CHECK) { + Jenkins.getInstance().checkPermission(Jenkins.READ); + } + return this; + } + + /** + * Escape hatch for StaplerProxy-based access control + */ + @Restricted(NoExternalUse.class) + public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(User.class.getName() + ".skipPermissionCheck"); + /** * Gets list of Illegal usernames, for which users should not be created. diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index 721c87476a7f910a4d8dd3aa50c3f53cbf49b364..50228d0647233e13b14035fbb4587df57b6766e5 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -1069,11 +1069,11 @@ public abstract class View extends AbstractModelObject implements AccessControll */ @Restricted(DoNotUse.class) public Categories doItemCategories(StaplerRequest req, StaplerResponse rsp, @QueryParameter String iconStyle) throws IOException, ServletException { + getOwner().checkPermission(Item.CREATE); rsp.addHeader("Cache-Control", "no-cache, no-store, must-revalidate"); rsp.addHeader("Pragma", "no-cache"); rsp.addHeader("Expires", "0"); - getOwner().checkPermission(Item.CREATE); Categories categories = new Categories(); int order = 0; JellyContext ctx; diff --git a/core/src/main/java/hudson/model/ViewDescriptor.java b/core/src/main/java/hudson/model/ViewDescriptor.java index a63484d69e1250aeb4ae2518e47b8e1a2e25b8b7..933ad74dcb68aeccb30cf329781e89ff185ba99e 100644 --- a/core/src/main/java/hudson/model/ViewDescriptor.java +++ b/core/src/main/java/hudson/model/ViewDescriptor.java @@ -88,7 +88,6 @@ public abstract class ViewDescriptor extends Descriptor { */ @Restricted(DoNotUse.class) public AutoCompletionCandidates doAutoCompleteCopyNewItemFrom(@QueryParameter final String value, @AncestorInPath ItemGroup container) { - // TODO do we need a permissions check here? AutoCompletionCandidates candidates = AutoCompletionCandidates.ofJobNames(TopLevelItem.class, value, container); if (container instanceof DirectlyModifiableTopLevelItemGroup) { DirectlyModifiableTopLevelItemGroup modifiableContainer = (DirectlyModifiableTopLevelItemGroup) container; diff --git a/core/src/main/java/hudson/search/Search.java b/core/src/main/java/hudson/search/Search.java index 627d77fafba4c570c86c12bac722b895764a3822..275a69536d2ac4b75d998f7e1d61678c2373cba9 100644 --- a/core/src/main/java/hudson/search/Search.java +++ b/core/src/main/java/hudson/search/Search.java @@ -42,10 +42,12 @@ import java.util.logging.Logger; import javax.servlet.ServletException; +import jenkins.model.Jenkins; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.Ancestor; import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerProxy; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.export.DataWriter; @@ -63,7 +65,7 @@ import org.kohsuke.stapler.export.Flavor; * @author Kohsuke Kawaguchi * @see SearchableModelObject */ -public class Search { +public class Search implements StaplerProxy { @Restricted(NoExternalUse.class) // used from stapler views only public static String encodeQuery(String query) throws UnsupportedEncodingException { return URLEncoder.encode(query, "UTF-8"); @@ -406,6 +408,21 @@ public class Search { return paths[tokens.length()]; } - + + @Override + @Restricted(NoExternalUse.class) + public Object getTarget() { + if (!SKIP_PERMISSION_CHECK) { + Jenkins.getInstance().checkPermission(Jenkins.READ); + } + return this; + } + + /** + * Escape hatch for StaplerProxy-based access control + */ + @Restricted(NoExternalUse.class) + public static /* Script Console modifiable */ boolean SKIP_PERMISSION_CHECK = Boolean.getBoolean(Search.class.getName() + ".skipPermissionCheck"); + private final static Logger LOGGER = Logger.getLogger(Search.class.getName()); } diff --git a/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java b/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java index a05fd59e8ebc58c7ab1d112318f8a2f709144390..958d63a3d53438d4281c52578086adb2602f2228 100644 --- a/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java +++ b/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java @@ -155,8 +155,6 @@ public class AdminWhitelistRule implements StaplerProxy { @RequirePOST public HttpResponse doSubmit(StaplerRequest req) throws IOException { - jenkins.checkPermission(Jenkins.RUN_SCRIPTS); - String whitelist = Util.fixNull(req.getParameter("whitelist")); if (!whitelist.endsWith("\n")) whitelist+="\n";