Merge pull request #99 from cloudbees/feature/UX-159

UX-159: Add a crumb exclusion for json
This commit is contained in:
Ivan Meredith 2016-04-11 15:45:35 +12:00
commit 79865fe196
5 changed files with 128 additions and 1 deletions

View File

@ -4,6 +4,7 @@ This is the BlueOcean repo. It is a multi-module maven project. Each sub-directo
Blue Ocean is the new UI project for Jenkins.
![Pirate logo, because it's ocean and stuff](logo-yarrr.png)
Yarr...

View File

@ -44,6 +44,70 @@ public class ProfileApiTest {
.body("fullName", Matchers.equalTo(system.getFullName()));
}
//UX-159
@Test
public void postCrumbTest() throws Exception {
User system = j.jenkins.getUser("SYSTEM");
RestAssured.given().contentType("application/json").log().all().post("/users/{id}/", system.getId())
.then().log().all()
.statusCode(200)
.body("id", Matchers.equalTo(system.getId()))
.body("fullName", Matchers.equalTo(system.getFullName()));
}
//UX-159
@Test
public void postCrumbFailTest() throws Exception {
User system = j.jenkins.getUser("SYSTEM");
RestAssured.given().log().all().post("/users/{id}/", system.getId())
.then().log().all()
.statusCode(403);
}
//UX-159
@Test
public void putMimeTest() throws Exception {
User system = j.jenkins.getUser("SYSTEM");
RestAssured.given().contentType("application/json").log().all().put("/users/{id}/", system.getId())
.then().log().all()
.statusCode(200)
.body("id", Matchers.equalTo(system.getId()))
.body("fullName", Matchers.equalTo(system.getFullName()));
}
@Test
public void putMimeFailTest() throws Exception {
User system = j.jenkins.getUser("SYSTEM");
RestAssured.given().log().all().put("/users/{id}/", system.getId())
.then().log().all()
.statusCode(415);
}
//UX-159
@Test
public void patchMimeTest() throws Exception {
User system = j.jenkins.getUser("SYSTEM");
RestAssured.given().contentType("application/json").log().all().patch("/users/{id}/", system.getId())
.then().log().all()
.statusCode(200)
.body("id", Matchers.equalTo(system.getId()))
.body("fullName", Matchers.equalTo(system.getFullName()));
}
@Test
public void patchMimeFailTest() throws Exception {
User system = j.jenkins.getUser("SYSTEM");
RestAssured.given().log().all().patch("/users/{id}/", system.getId())
.then().log().all()
.statusCode(415);
}
@Test
public void getUserDetailsTest() throws Exception {
hudson.model.User user = j.jenkins.getUser("alice");

View File

@ -1,5 +1,10 @@
# Usage
## Crumbs
Jenkins usually requires a "crumb" with posted reuqests to prevent request forgery and other shenanigans.
To avoid needing a crumb to POST data, the header `Content-Type: application/json` *must* be used.
## Run BlueOcean plugin
cd bluecoean-plugin
@ -16,7 +21,6 @@ BlueOcean rest API base URL is:
http://localhost:8080/jenkins/blue/rest
## Get a user
curl -v -X GET http://localhost:8080/jenkins/blue/rest/users/alice

View File

@ -0,0 +1,47 @@
package io.jenkins.blueocean.rest;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.security.csrf.CrumbExclusion;
import io.jenkins.blueocean.RootRoutable;
/**
* This class forces the Blueocean API to require json for POSTs so that we do not need a crumb.
* @author Ivan Meredith
*/
@Extension
public class APICrumbExclusion extends CrumbExclusion{
@Override
public boolean process(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws IOException, ServletException {
String pathInfo = httpServletRequest.getPathInfo();
for (RootRoutable r : ExtensionList.lookup(RootRoutable.class)) {
String path = getExclusionPath(r.getUrlName());
if (pathInfo != null && pathInfo.startsWith(path)) {
String header = httpServletRequest.getHeader("Content-Type");
if(header != null && header.contains("application/json")) {
filterChain.doFilter(httpServletRequest, httpServletResponse);
return true;
} else {
return false;
}
}
}
return false;
}
public String getExclusionPath(String route) {
return "/blue/" + route + "/";
}
}

View File

@ -3,10 +3,13 @@ package io.jenkins.blueocean.rest;
import hudson.Extension;
import hudson.ExtensionList;
import io.jenkins.blueocean.RootRoutable;
import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.rest.pageable.Pageable;
import io.jenkins.blueocean.rest.pageable.Pageables;
import io.jenkins.blueocean.rest.pageable.PagedResponse;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.WebMethod;
import org.kohsuke.stapler.verb.GET;
@ -64,6 +67,14 @@ public final class ApiHead implements RootRoutable {
* @return {@link ApiRoutable} object
*/
public ApiRoutable getDynamic(String route) {
StaplerRequest request = Stapler.getCurrentRequest();
String m = request.getMethod();
if(m.equalsIgnoreCase("POST") || m.equalsIgnoreCase("PUT") || m.equalsIgnoreCase("PATCH")) {
String header = request.getHeader("Content-Type");
if(header == null || !header.contains("application/json")) {
throw new ServiceException(415, "Content-Type: application/json required");
}
}
return apis.get(route);
}