提交 acff3310 编写于 作者: J Jesse Glick

[FIXED JENKINS-16936] Added SecureRequester extension point.

上级 587c6fe3
......@@ -58,6 +58,9 @@ Upcoming changes</a>
<li class='rfe'>
Upgrade bundled plugin versions: ssh-slaves to 1.5, and credentials to 1.9.1
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-20071">issue 20071</a>)
<li class=rfe>
Extension point for secure users of REST APIs (permitting JSONP and primitive XPath).
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-16936">issue 16936</a>)
<li class=bug>
Integer overflow could cause JavaScript functions to break in long-running Jenkins processes.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-20085">issue 20085</a>)
......
......@@ -25,6 +25,8 @@ package hudson.model;
import hudson.util.IOException2;
import jenkins.model.Jenkins;
import jenkins.security.SecureRequester;
import org.dom4j.CharacterData;
import org.dom4j.Document;
import org.dom4j.DocumentException;
......@@ -59,6 +61,7 @@ import java.util.logging.Logger;
*
* @author Kohsuke Kawaguchi
* @see Exported
* @see SecureRequester
*/
public class Api extends AbstractModelObject {
/**
......@@ -155,12 +158,12 @@ public class Api extends AbstractModelObject {
OutputStream o = rsp.getCompressedOutputStream(req);
try {
if (result instanceof CharacterData || result instanceof String || result instanceof Number || result instanceof Boolean) {
if (INSECURE) {
if (permit(req)) {
rsp.setContentType("text/plain;charset=UTF-8");
String text = result instanceof CharacterData ? ((CharacterData) result).getText() : result.toString();
o.write(text.getBytes("UTF-8"));
} else {
rsp.sendError(HttpURLConnection.HTTP_FORBIDDEN, "primitive XPath result sets forbidden; can use -Dhudson.model.Api.INSECURE=true if you run without security");
rsp.sendError(HttpURLConnection.HTTP_FORBIDDEN, "primitive XPath result sets forbidden; implement jenkins.security.SecureRequester");
}
return;
}
......@@ -188,11 +191,11 @@ public class Api extends AbstractModelObject {
* Exposes the bean as JSON.
*/
public void doJson(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
if (INSECURE || req.getParameter("jsonp") == null) {
setHeaders(rsp);
if (req.getParameter("jsonp") == null || permit(req)) {
setHeaders(rsp);
rsp.serveExposedBean(req,bean, Flavor.JSON);
} else {
rsp.sendError(HttpURLConnection.HTTP_FORBIDDEN, "jsonp forbidden; can use -Dhudson.model.Api.INSECURE=true if you run without security");
rsp.sendError(HttpURLConnection.HTTP_FORBIDDEN, "jsonp forbidden; implement jenkins.security.SecureRequester");
}
}
......@@ -204,6 +207,15 @@ public class Api extends AbstractModelObject {
rsp.serveExposedBean(req,bean, Flavor.PYTHON);
}
private boolean permit(StaplerRequest req) {
for (SecureRequester r : Jenkins.getInstance().getExtensionList(SecureRequester.class)) {
if (r.permit(req, bean)) {
return true;
}
}
return false;
}
private void setHeaders(StaplerResponse rsp) {
rsp.setHeader("X-Jenkins", Jenkins.VERSION);
rsp.setHeader("X-Jenkins-Session", Jenkins.SESSION_HASH);
......@@ -211,6 +223,5 @@ public class Api extends AbstractModelObject {
private static final Logger LOGGER = Logger.getLogger(Api.class.getName());
private static final ModelBuilder MODEL_BUILDER = new ModelBuilder();
private static final boolean INSECURE = "true".equals(System.getProperty("hudson.model.Api.INSECURE"));
}
package jenkins.security;
import hudson.Extension;
import hudson.ExtensionPoint;
import hudson.model.Api;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.StaplerRequest;
/**
* An extension point for authorizing REST API access to an object where an unsafe result type would be produced.
* Both JSONP and XPath with primitive result sets are considered unsafe due to CSRF attacks.
* A default implementation allows requests if a deprecated system property is set, or if Jenkins is unsecured anyway,
* but plugins may offer implementations which authorize scripted clients, requests from inside a trusted domain, etc.
* @see Api
* @since 1.537
*/
public interface SecureRequester extends ExtensionPoint {
/**
* Checks if a Jenkins object can be accessed by a given REST request.
* For instance, if the {@link StaplerRequest#getReferer} matches a given host, or
* anonymous read is allowed for the given object.
* @param req a request going through the REST API
* @param bean an exported object of some kind
* @return true if this requester should be trusted, false to reject
*/
boolean permit(StaplerRequest req, Object bean);
@Restricted(NoExternalUse.class)
@Extension class Default implements SecureRequester {
private static final String PROP = "hudson.model.Api.INSECURE";
private static final boolean INSECURE = Boolean.getBoolean(PROP);
static {
if (INSECURE) {
Logger.getLogger(SecureRequester.class.getName()).warning(PROP + " system property is deprecated; implement SecureRequester instead");
}
}
@Override public boolean permit(StaplerRequest req, Object bean) {
return INSECURE || !Jenkins.getInstance().isUseSecurity();
}
}
}
\ No newline at end of file
......@@ -11,6 +11,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage;
import java.net.HttpURLConnection;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.HudsonTestCase;
import org.jvnet.hudson.test.recipes.PresetData;
/**
*
......@@ -97,6 +98,7 @@ public class DefaultCrumbIssuerTest extends HudsonTestCase {
submit(p.getFormByName("config"));
}
@PresetData(PresetData.DataSet.ANONYMOUS_READONLY)
public void testApiXml() throws Exception {
WebClient wc = new WebClient();
assertXPathValue(wc.goToXml("crumbIssuer/api/xml"), "//crumbRequestField", jenkins.getCrumbIssuer().getCrumbRequestField());
......@@ -116,6 +118,7 @@ public class DefaultCrumbIssuerTest extends HudsonTestCase {
wc.assertFails("crumbIssuer/api/xml?xpath=concat(//crumbRequestField,'=',//crumb)", HttpURLConnection.HTTP_FORBIDDEN); // perhaps interpretable as JS number
}
@PresetData(PresetData.DataSet.ANONYMOUS_READONLY)
public void testApiJson() throws Exception {
WebClient wc = new WebClient();
String json = wc.goTo("crumbIssuer/api/json", "application/json").getWebResponse().getContentAsString();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册