Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
</developer>
</developers>

<dependencies>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>3.2.2</version>
</dependency>
</dependencies>

<scm>
<connection>scm:git:git://github.com/jenkinsci/${project.artifactId}-plugin.git</connection>
<developerConnection>scm:git:[email protected]:jenkinsci/${project.artifactId}-plugin.git</developerConnection>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.logging.Logger;
import org.apache.commons.lang.StringUtils;

/**
* Filter to support <a href="http://en.wikipedia.org/wiki/Cross-origin_resource_sharing">CORS</a>
Expand All @@ -35,6 +37,7 @@ public class AccessControlsFilter implements Filter, Describable<AccessControlsF
private static final Logger LOGGER = Logger.getLogger(AccessControlsFilter.class.getCanonicalName());
private static final String PREFLIGHT_REQUEST = "OPTIONS";
private List<String> allowedOriginsList = null;
private List<String> allowedHeadersList = null;

@Initializer(after = InitMilestone.JOB_LOADED)
public static void init() throws ServletException {
Expand Down Expand Up @@ -88,9 +91,53 @@ private void processAccessControls(HttpServletRequest req, HttpServletResponse r
resp.addHeader("Access-Control-Allow-Methods", getDescriptor().getAllowedMethods());
resp.addHeader("Access-Control-Allow-Credentials", "true");
resp.addHeader("Access-Control-Allow-Origin", origin);

/**
* Requested headers
*/
String requestedHeaders = req.getHeader("Access-Control-Request-Headers");
if (requestedHeaders != null && !requestedHeaders.trim().isEmpty()) {
List<String> acceptedHeadersList = processRequestedHeaders(Arrays.asList(requestedHeaders.split("\\s*,\\s*")));
if (!acceptedHeadersList.isEmpty()) {
// JAVA 8+
//resp.addHeader("Access-Control-Allow-Headers", String.join(", ", acceptedHeadersList));
// JAVA 7
if (acceptedHeadersList.size() == 1) {
resp.addHeader("Access-Control-Allow-Headers", acceptedHeadersList.get(0));
} else {
StringBuilder sb = new StringBuilder();
sb.append(acceptedHeadersList.get(0));
for (int i = 1; i < acceptedHeadersList.size(); i++) {
sb.append(", ").append(acceptedHeadersList.get(i));
}
resp.addHeader("Access-Control-Allow-Headers", sb.toString());
}
}
}
}
}

private List<String> processRequestedHeaders(List<String> requestedList) {
List<String> acceptedList = new ArrayList<String>();

if (allowedHeadersList == null) {
String allowedHeaders = getDescriptor().getAllowedHeaders();
if (allowedHeaders != null && !allowedHeaders.trim().isEmpty()) {
allowedHeadersList = Arrays.asList(allowedHeaders.split("\\s*,\\s*"));
} else {
allowedHeadersList = Collections.EMPTY_LIST;
}
}

for (int i = 0; i < requestedList.size(); i++) {
if (allowedHeadersList.contains(requestedList.get(i))) {
acceptedList.add(requestedList.get(i));
}
}

return acceptedList;
}

/**
* Check if the origin is allowed
*
Expand Down Expand Up @@ -141,6 +188,7 @@ public static final class DescriptorImpl extends Descriptor<AccessControlsFilter
private boolean enabled;
private String allowedOrigins;
private String allowedMethods;
private String allowedHeaders;

public DescriptorImpl() {
load();
Expand All @@ -156,6 +204,7 @@ public boolean configure(StaplerRequest req, JSONObject json) throws FormExcepti
enabled = json.getBoolean("enabled");
allowedOrigins = json.getString("allowedOrigins");
allowedMethods = json.getString("allowedMethods");
allowedHeaders = json.getString("allowedHeaders");

save();
return super.configure(req, json);
Expand Down Expand Up @@ -184,5 +233,13 @@ public String getAllowedMethods() {
public void setAllowedMethods(String allowedMethods) {
this.allowedMethods = allowedMethods;
}

public String getAllowedHeaders() {
return allowedHeaders;
}

public void setAllowedHeaders(String allowedHeaders) {
this.allowedHeaders = allowedHeaders;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@
<f:entry title="Access-Control-Allow-Methods" field="allowedMethods">
<f:textbox/>
</f:entry>
<f:entry title="Access-Control-Allow-Headers" field="allowedHeaders">
<f:textbox/>
</f:entry>
</f:section>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div>
Specifies the header or headers allowed when accessing the Jenkins resources. This is used in response to a pre-flight
request containing Access-Control-Request-Headers

For example: Content-Type
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,19 @@ public void testAllowOrigins() throws Exception {
assertEquals(htmlPage.getWebResponse().getResponseHeaderValue("Access-Control-Allow-Origin"), "http://localhost:9000");
}

@Test
public void testAllowHeaders() throws Exception {
descriptor.setAllowedMethods("GET, OPTIONS");
descriptor.setAllowedOrigins("http://localhost:9000, http://localhost:8080");
descriptor.setAllowedHeaders("Origin, Content-Type, X-Foo");
descriptor.setEnabled(true);

client.addRequestHeader("Origin", "http://localhost:9000");
client.addRequestHeader("Access-Control-Request-Headers", "Content-Type");
HtmlPage htmlPage = client.goTo("");

assertTrue(Boolean.valueOf(htmlPage.getWebResponse().getResponseHeaderValue("Access-Control-Allow-Credentials")));
assertEquals(htmlPage.getWebResponse().getResponseHeaderValue("Access-Control-Allow-Headers"), "Content-Type");
}

}