diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile index 387e9c22..26b5f722 100644 --- a/src/main/docker/Dockerfile +++ b/src/main/docker/Dockerfile @@ -1,5 +1,4 @@ -# api calls 403 error with jenkins 2.176.2+/2.186+ because of security changes: https://jenkins.io/security/advisory/2019-07-17/#SECURITY-626 -ARG jenkins_tag=2.176.1-alpine +ARG jenkins_tag=2.190.3-alpine FROM jenkins/jenkins:$jenkins_tag diff --git a/src/main/java/com/cdancy/jenkins/rest/JenkinsConstants.java b/src/main/java/com/cdancy/jenkins/rest/JenkinsConstants.java index b0263214..a5259744 100644 --- a/src/main/java/com/cdancy/jenkins/rest/JenkinsConstants.java +++ b/src/main/java/com/cdancy/jenkins/rest/JenkinsConstants.java @@ -41,6 +41,8 @@ public class JenkinsConstants { public static final String OPTIONAL_FOLDER_PATH_PARAM = "optionalFolderPath"; + public static final String JENKINS_COOKIES_JSESSIONID = "JSESSIONID"; + protected JenkinsConstants() { throw new UnsupportedOperationException("Purposefully not implemented"); } diff --git a/src/main/java/com/cdancy/jenkins/rest/domain/crumb/Crumb.java b/src/main/java/com/cdancy/jenkins/rest/domain/crumb/Crumb.java index 6227feb7..b0125e7a 100644 --- a/src/main/java/com/cdancy/jenkins/rest/domain/crumb/Crumb.java +++ b/src/main/java/com/cdancy/jenkins/rest/domain/crumb/Crumb.java @@ -24,18 +24,34 @@ import com.cdancy.jenkins.rest.domain.common.Error; import com.cdancy.jenkins.rest.domain.common.ErrorsHolder; -import com.cdancy.jenkins.rest.domain.common.Value; import com.cdancy.jenkins.rest.JenkinsUtils; import com.google.auto.value.AutoValue; @AutoValue -public abstract class Crumb implements Value, ErrorsHolder { +public abstract class Crumb implements ErrorsHolder { + + @Nullable + public abstract String value(); + + @Nullable + public abstract String sessionIdCookie(); @SerializedNames({ "value", "errors" }) - public static Crumb create(@Nullable final String value, + public static Crumb create(final String value, + final List errors) { + + return create(value, null, errors); + } + + @SerializedNames({ "value", "sessionIdCookie" }) + public static Crumb create(final String value, final String sessionIdCookie) { + return create(value, sessionIdCookie, null); + } + + private static Crumb create(final String value, final String sessionIdCookie, final List errors) { - return new AutoValue_Crumb(value, - JenkinsUtils.nullToEmpty(errors)); + return new AutoValue_Crumb(JenkinsUtils.nullToEmpty(errors), value, + sessionIdCookie); } } diff --git a/src/main/java/com/cdancy/jenkins/rest/filters/JenkinsAuthenticationFilter.java b/src/main/java/com/cdancy/jenkins/rest/filters/JenkinsAuthenticationFilter.java index 80e8ade1..b743d08d 100644 --- a/src/main/java/com/cdancy/jenkins/rest/filters/JenkinsAuthenticationFilter.java +++ b/src/main/java/com/cdancy/jenkins/rest/filters/JenkinsAuthenticationFilter.java @@ -17,6 +17,7 @@ package com.cdancy.jenkins.rest.filters; +import java.util.Optional; import javax.inject.Inject; import javax.inject.Singleton; @@ -39,7 +40,7 @@ public class JenkinsAuthenticationFilter implements HttpRequestFilter { private final JenkinsApi jenkinsApi; // key = Crumb, value = true if exception is ResourceNotFoundException false otherwise - private static volatile Pair crumbPair = null; + private static volatile Pair crumbPair = null; private static final String CRUMB_HEADER = "Jenkins-Crumb"; @Inject @@ -61,6 +62,8 @@ public HttpRequest filter(final HttpRequest request) throws HttpException { final Pair localCrumb = getCrumb(); if (localCrumb.getKey().value() != null) { builder.addHeader(CRUMB_HEADER, localCrumb.getKey().value()); + Optional.ofNullable(localCrumb.getKey().sessionIdCookie()) + .ifPresent(sessionId -> builder.addHeader(HttpHeaders.COOKIE, sessionId)); } else { if (localCrumb.getValue() == false) { throw new RuntimeException("Unexpected exception being thrown: error=" + localCrumb.getKey().errors().get(0)); @@ -87,7 +90,7 @@ private Pair getCrumb() { } return crumbValueInit; } - + // simple impl/copy of javafx.util.Pair private class Pair { private final A a; diff --git a/src/main/java/com/cdancy/jenkins/rest/parsers/CrumbParser.java b/src/main/java/com/cdancy/jenkins/rest/parsers/CrumbParser.java index ea1a6188..92853ac7 100644 --- a/src/main/java/com/cdancy/jenkins/rest/parsers/CrumbParser.java +++ b/src/main/java/com/cdancy/jenkins/rest/parsers/CrumbParser.java @@ -17,12 +17,15 @@ package com.cdancy.jenkins.rest.parsers; +import static com.cdancy.jenkins.rest.JenkinsConstants.JENKINS_COOKIES_JSESSIONID; + import com.cdancy.jenkins.rest.domain.crumb.Crumb; import com.google.common.base.Function; import java.io.IOException; import javax.inject.Singleton; +import javax.ws.rs.core.HttpHeaders; import org.jclouds.http.HttpResponse; import org.jclouds.util.Strings2; @@ -38,8 +41,7 @@ public Crumb apply(final HttpResponse input) { final int statusCode = input.getStatusCode(); if (statusCode >= 200 && statusCode < 400) { try { - final String response = Strings2.toStringAndClose(input.getPayload().openStream()); - return Crumb.create(response.split(":")[1], null); + return Crumb.create(crumbValue(input), sessionIdCookie(input)); } catch (final IOException e) { throw new RuntimeException(input.getStatusLine(), e); } @@ -47,4 +49,16 @@ public Crumb apply(final HttpResponse input) { throw new RuntimeException(input.getStatusLine()); } } + + private static String crumbValue(HttpResponse input) throws IOException { + return Strings2.toStringAndClose(input.getPayload().openStream()) + .split(":")[1]; + } + + private static String sessionIdCookie(HttpResponse input) { + return input.getHeaders().get(HttpHeaders.SET_COOKIE).stream() + .filter(c -> c.startsWith(JENKINS_COOKIES_JSESSIONID)) + .findFirst() + .orElse(""); + } }