From c2784c3246ec5ed4dbbf1075b08d581230c44412 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Sat, 7 Apr 2018 16:04:16 +0200 Subject: [PATCH 01/19] Converted for loop to java8 lambda --- .../org/jenkinsci/plugins/gogs/GogsUtils.java | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java index 04110c0..5cc7858 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java @@ -5,28 +5,25 @@ class GogsUtils { - private GogsUtils() { - } + private GogsUtils() { + } + + /** + * Search in Jenkins for a item with type T based on the job name + * + * @param jobName job to find, for jobs inside a folder use : {@literal //} + * @return the Job matching the given name, or {@code null} when not found + */ + static T find(String jobName, Class type) { + Jenkins jenkins = Jenkins.getActiveInstance(); + // direct search, can be used to find folder based items // + T item = jenkins.getItemByFullName(jobName, type); + if (item == null) { + // not found in a direct search, search in all items since the item might be in a folder but given without folder structure + // (to keep it backwards compatible) + item = jenkins.getAllItems(type).stream().filter(i -> i.getName().equals(jobName)).findFirst().get(); + } + return item; + } - /** - * Search in Jenkins for a item with type T based on the job name - * @param jobName job to find, for jobs inside a folder use : {@literal //} - * @return the Job matching the given name, or {@code null} when not found - */ - static T find(String jobName, Class type) { - Jenkins jenkins = Jenkins.getActiveInstance(); - // direct search, can be used to find folder based items // - T item = jenkins.getItemByFullName(jobName, type); - if (item == null) { - // not found in a direct search, search in all items since the item might be in a folder but given without folder structure - // (to keep it backwards compatible) - for (T allItem : jenkins.getAllItems(type)) { - if (allItem.getName().equals(jobName)) { - item = allItem; - break; - } - } - } - return item; - } } From fd099954282929c715519cd4fecba447f1bc8fb7 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Sat, 7 Apr 2018 16:37:37 +0200 Subject: [PATCH 02/19] Remove more for loops --- .../java/org/jenkinsci/plugins/gogs/GogsPayload.java | 5 +---- .../org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java | 9 ++------- .../java/org/jenkinsci/plugins/gogs/GogsWebHook.java | 4 +++- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayload.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayload.java index fb0ffc8..95e2b9f 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayload.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayload.java @@ -25,10 +25,7 @@ private Map getPayload() { @Override public void buildEnvVars(AbstractBuild abstractBuild, EnvVars envVars) { LOGGER.log(Level.FINEST, "Injecting GOGS_PAYLOAD: {0}", getPayload()); - for (Map.Entry entry : payload.entrySet()) { - envVars.put("GOGS_" + entry.getKey().toUpperCase(), entry.getValue()); - } - + payload.forEach((key, value) -> envVars.put("GOGS_" + key.toUpperCase(), value)); } private static final Logger LOGGER = Logger.getLogger(GogsPayload.class.getName()); diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java index 5c7be42..00cca04 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java @@ -26,7 +26,6 @@ associated documentation files (the "Software"), to deal in the Software without import hudson.model.BuildableItem; import hudson.model.Cause; import hudson.security.ACL; -import hudson.triggers.Trigger; import jenkins.model.ParameterizedJobMixIn; import jenkins.triggers.SCMTriggerItem; import org.acegisecurity.context.SecurityContext; @@ -61,12 +60,8 @@ public GogsResults triggerJobs(String jobName, String deliveryID) { if (project instanceof ParameterizedJobMixIn.ParameterizedJob) { ParameterizedJobMixIn.ParameterizedJob pJob = (ParameterizedJobMixIn.ParameterizedJob) project; - for (Trigger trigger : pJob.getTriggers().values()) { - if (trigger instanceof GogsTrigger) { - gTrigger = (GogsTrigger) trigger; - break; - } - } + gTrigger = (GogsTrigger) pJob.getTriggers().values().stream() + .filter(trigger1 -> trigger1 instanceof GogsTrigger).findFirst().get(); } if (gTrigger != null) { diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java index 6137ec2..e2c679b 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java @@ -35,6 +35,7 @@ associated documentation files (the "Software"), to deal in the Software without import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; +import javax.annotation.Nonnull; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.IOException; @@ -79,7 +80,8 @@ public String getUrlName() { * @return a String with the encoded sha256 hmac * @throws Exception Something went wrong getting the sha256 hmac */ - private static String encode(String data, String key) throws Exception { + private static @Nonnull + String encode(String data, String key) throws Exception { final Charset asciiCs = Charset.forName("UTF-8"); final Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); final SecretKeySpec secret_key = new javax.crypto.spec.SecretKeySpec(asciiCs.encode(key).array(), "HmacSHA256"); From 24dd95b8f36b932c63aa25f8d70dd1064889f518 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Sat, 7 Apr 2018 17:29:53 +0200 Subject: [PATCH 03/19] More java 8 stuff --- .../plugins/gogs/GogsPayloadProcessor.java | 38 +++++++++++-------- .../org/jenkinsci/plugins/gogs/GogsUtils.java | 4 +- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java index 00cca04..f7c5a87 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java @@ -35,6 +35,8 @@ associated documentation files (the "Software"), to deal in the Software without import java.io.StringWriter; import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; class GogsPayloadProcessor { @@ -49,35 +51,32 @@ public void setPayload(String k, String v) { } public GogsResults triggerJobs(String jobName, String deliveryID) { + AtomicBoolean jobdone = new AtomicBoolean(false); SecurityContext saveCtx = ACL.impersonate(ACL.SYSTEM); GogsResults result = new GogsResults(); try { BuildableItem project = GogsUtils.find(jobName, BuildableItem.class); if (project != null) { - GogsTrigger gTrigger = null; Cause cause = new GogsCause(deliveryID); if (project instanceof ParameterizedJobMixIn.ParameterizedJob) { ParameterizedJobMixIn.ParameterizedJob pJob = (ParameterizedJobMixIn.ParameterizedJob) project; - gTrigger = (GogsTrigger) pJob.getTriggers().values().stream() - .filter(trigger1 -> trigger1 instanceof GogsTrigger).findFirst().get(); + pJob.getTriggers().values().stream() + .filter(trigger1 -> trigger1 instanceof GogsTrigger).findFirst() + .ifPresent((g) -> { + GogsPayload gogsPayload = new GogsPayload(this.payload); + Optional.ofNullable(SCMTriggerItem.SCMTriggerItems.asSCMTriggerItem(project)) + .ifPresent((item) -> { + item.scheduleBuild2(0, gogsPayload); + jobdone.set(true); + }); + }); } - - if (gTrigger != null) { - SCMTriggerItem item = SCMTriggerItem.SCMTriggerItems.asSCMTriggerItem(project); - GogsPayload gogsPayload = new GogsPayload(this.payload); - if (item != null) { - item.scheduleBuild2(0, gogsPayload); - } - } else { + if (!jobdone.get()) { project.scheduleBuild(0, cause); + jobdone.set(true); } - result.setMessage(String.format("Job '%s' is executed", jobName)); - } else { - String msg = String.format("Job '%s' is not defined in Jenkins", jobName); - result.setStatus(404, msg); - LOGGER.warning(msg); } } catch (Exception e) { StringWriter sw = new StringWriter(); @@ -85,6 +84,13 @@ public GogsResults triggerJobs(String jobName, String deliveryID) { e.printStackTrace(pw); LOGGER.severe(sw.toString()); } finally { + if (jobdone.get()) { + result.setMessage(String.format("Job '%s' is executed", jobName)); + } else { + String msg = String.format("Job '%s' is not defined in Jenkins", jobName); + result.setStatus(404, msg); + LOGGER.warning(msg); + } SecurityContextHolder.setContext(saveCtx); } diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java index 5cc7858..2a348f3 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java @@ -7,7 +7,7 @@ class GogsUtils { private GogsUtils() { } - + /** * Search in Jenkins for a item with type T based on the job name * @@ -21,7 +21,7 @@ static T find(String jobName, Class type) { if (item == null) { // not found in a direct search, search in all items since the item might be in a folder but given without folder structure // (to keep it backwards compatible) - item = jenkins.getAllItems(type).stream().filter(i -> i.getName().equals(jobName)).findFirst().get(); + item = jenkins.getAllItems(type).stream().filter(i -> i.getName().equals(jobName)).findFirst().orElse(null); } return item; } From cb0441bb9895fa3f69270cc5560effdebb7664a6 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Sat, 7 Apr 2018 18:38:01 +0200 Subject: [PATCH 04/19] Simplify code --- .../jenkinsci/plugins/gogs/GogsWebHook.java | 34 ++++++------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java index e2c679b..77eaef1 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java @@ -40,16 +40,17 @@ associated documentation files (the "Software"), to deal in the Software without import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.Charset; import java.util.Arrays; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Strings.isNullOrEmpty; /** * @author Alexander Verhaar @@ -114,7 +115,7 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException // Get X-Gogs-Delivery header with deliveryID String gogsDelivery = req.getHeader("X-Gogs-Delivery"); - if (gogsDelivery == null || gogsDelivery.isEmpty()) { + if (isNullOrEmpty(gogsDelivery)) { gogsDelivery = "Triggered by Jenkins-Gogs-Plugin. Delivery ID unknown."; } else { gogsDelivery = "Gogs-ID: " + gogsDelivery; @@ -122,7 +123,7 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException // Get X-Gogs-Signature String gogsSignature = req.getHeader("X-Gogs-Signature"); - if (gogsSignature == null || gogsSignature.isEmpty()) { + if (isNullOrEmpty(gogsSignature)) { gogsSignature = null; } @@ -137,14 +138,11 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException exitWebHook(result, rsp); return; } - Object jobObject = queryStringMap.get("job"); - String jobName; - if (jobObject == null) { + String jobName = queryStringMap.get("job").toString(); + if (isNullOrEmpty(jobName)) { result.setStatus(404, "No value assigned to parameter 'job'"); exitWebHook(result, rsp); return; - } else { - jobName = jobObject.toString(); } // Get the POST stream @@ -272,20 +270,10 @@ private void exitWebHook(GogsResults result, StaplerResponse resp) throws IOExce * @param qs Querystring * @return returns map from querystring */ - private static Map splitQuery(String qs) throws UnsupportedEncodingException { - final Map query_pairs = new LinkedHashMap<>(); - final String[] pairs = qs.split("&"); - for (String pair : pairs) { - final int idx = pair.indexOf("="); - final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), DEFAULT_CHARSET) : pair; - final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), DEFAULT_CHARSET) : null; - query_pairs.put(key, value); - } - return query_pairs; - } - - private boolean isNullOrEmpty(String s) { - return s == null || s.trim().isEmpty(); + private static Map splitQuery(String qs) { + return Pattern.compile("&").splitAsStream(qs) + .map(p -> p.split("=")) + .collect(Collectors.toMap(a -> a[0], a -> a.length > 1 ? a[1] : "")); } } From 2cd608da7d85849b89c2aa1fe3cacb85980bdba6 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Sat, 7 Apr 2018 18:15:59 +0200 Subject: [PATCH 05/19] Remove duplicated code --- .../jenkinsci/plugins/gogs/GogsWebHook.java | 52 ++++++++----------- .../plugins/gogs/GogsWebHookTest.java | 2 - 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java index 77eaef1..283e50b 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java @@ -45,6 +45,8 @@ associated documentation files (the "Software"), to deal in the Software without import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -167,45 +169,35 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException body = body.substring(8); } - String jSecret = null; - boolean foundJob = false; + AtomicReference jSecret = new AtomicReference<>(null); + AtomicBoolean foundJob = new AtomicBoolean(false); payloadProcessor.setPayload("ref", jsonObject.getString("ref")); payloadProcessor.setPayload("before", jsonObject.getString("before")); SecurityContext saveCtx = ACL.impersonate(ACL.SYSTEM); try { - Job job = GogsUtils.find(jobName, Job.class); - - if (job != null) { - foundJob = true; - /* secret is stored in the properties of Job */ - final GogsProjectProperty property = (GogsProjectProperty) job.getProperty(GogsProjectProperty.class); - if (property != null) { /* only if Gogs secret is defined on the job */ - jSecret = property.getGogsSecret(); /* Secret provided by Jenkins */ - } + String ref = (String) jsonObject.get("ref"); + String[] components = ref.split("/"); + if (components.length > 3) { + /* refs contains branch/tag with a slash */ + List test = Arrays.asList(ref.split("/")); + ref = String.join("%2F", test.subList(2, test.size())); } else { - String ref = (String) jsonObject.get("ref"); - String[] components = ref.split("/"); - if (components.length > 3) { - /* refs contains branch/tag with a slash */ - List test = Arrays.asList(ref.split("/")); - ref = String.join("%2F", test.subList(2, test.size())); - } else { - ref = components[components.length - 1]; - } - - job = GogsUtils.find(jobName + "/" + ref, Job.class); + ref = components[components.length - 1]; + } + Arrays.asList(jobName, jobName + "/" + ref).forEach(j -> { + Job job = GogsUtils.find(j, Job.class); if (job != null) { - foundJob = true; + foundJob.set(true); /* secret is stored in the properties of Job */ final GogsProjectProperty property = (GogsProjectProperty) job.getProperty(GogsProjectProperty.class); if (property != null) { /* only if Gogs secret is defined on the job */ - jSecret = property.getGogsSecret(); /* Secret provided by Jenkins */ + jSecret.set(property.getGogsSecret()); /* Secret provided by Jenkins */ } } - } + }); } finally { SecurityContextHolder.setContext(saveCtx); } @@ -215,8 +207,8 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException gSecret = jsonObject.optString("secret", null); /* Secret provided by Gogs < 0.10.x */ } else { try { - if (gogsSignature.equals(encode(body, jSecret))) { - gSecret = jSecret; + if (gogsSignature.equals(encode(body, jSecret.get()))) { + gSecret = jSecret.get(); // now hex is right, continue to old logic } } catch (Exception e) { @@ -224,14 +216,14 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException } } - if (!foundJob) { + if (!foundJob.get()) { String msg = String.format("Job '%s' is not defined in Jenkins", jobName); result.setStatus(404, msg); LOGGER.warning(msg); - } else if (isNullOrEmpty(jSecret) && isNullOrEmpty(gSecret)) { + } else if (isNullOrEmpty(jSecret.get()) && isNullOrEmpty(gSecret)) { /* No password is set in Jenkins and Gogs, run without secrets */ result = payloadProcessor.triggerJobs(jobName, gogsDelivery); - } else if (!isNullOrEmpty(jSecret) && jSecret.equals(gSecret)) { + } else if (!isNullOrEmpty(jSecret.get()) && jSecret.equals(gSecret)) { /* Password is set in Jenkins and Gogs, and is correct */ result = payloadProcessor.triggerJobs(jobName, gogsDelivery); } else { diff --git a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java index bffbd1b..13ff144 100644 --- a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java +++ b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java @@ -235,8 +235,6 @@ public void whenUriDoesNotContainUrlNameMustReturnError() throws Exception { // // Helper methods // - - private void performDoIndexTest(StaplerRequest staplerRequest, StaplerResponse staplerResponse, File file) throws IOException { PrintWriter printWriter = new PrintWriter(file.getAbsoluteFile(), "UTF-8"); when(staplerResponse.getWriter()).thenReturn(printWriter); From 532792c1f9960fa72e75f3d4ae977a1e98c51ab6 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Sun, 8 Apr 2018 01:18:51 +0200 Subject: [PATCH 06/19] Refactor splitting of refs --- .../org/jenkinsci/plugins/gogs/GogsWebHook.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java index 283e50b..4f56512 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java @@ -43,8 +43,8 @@ associated documentation files (the "Software"), to deal in the Software without import java.net.URLDecoder; import java.nio.charset.Charset; import java.util.Arrays; -import java.util.List; import java.util.Map; +import java.util.StringJoiner; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; @@ -177,15 +177,10 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException SecurityContext saveCtx = ACL.impersonate(ACL.SYSTEM); try { - String ref = (String) jsonObject.get("ref"); - String[] components = ref.split("/"); - if (components.length > 3) { - /* refs contains branch/tag with a slash */ - List test = Arrays.asList(ref.split("/")); - ref = String.join("%2F", test.subList(2, test.size())); - } else { - ref = components[components.length - 1]; - } + StringJoiner stringJoiner = new StringJoiner("%2F"); + Pattern.compile("/").splitAsStream((String) jsonObject.get("ref")).skip(2) + .forEach(stringJoiner::add); + String ref = stringJoiner.toString(); Arrays.asList(jobName, jobName + "/" + ref).forEach(j -> { Job job = GogsUtils.find(j, Job.class); @@ -223,7 +218,7 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException } else if (isNullOrEmpty(jSecret.get()) && isNullOrEmpty(gSecret)) { /* No password is set in Jenkins and Gogs, run without secrets */ result = payloadProcessor.triggerJobs(jobName, gogsDelivery); - } else if (!isNullOrEmpty(jSecret.get()) && jSecret.equals(gSecret)) { + } else if (!isNullOrEmpty(jSecret.get()) && jSecret.get().equals(gSecret)) { /* Password is set in Jenkins and Gogs, and is correct */ result = payloadProcessor.triggerJobs(jobName, gogsDelivery); } else { From 9cb56806f42289940c2bb8e3c8f72466a8e13981 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Sun, 8 Apr 2018 02:23:43 +0200 Subject: [PATCH 07/19] Some more refactoring --- .../org/jenkinsci/plugins/gogs/GogsUtils.java | 37 ++++ .../jenkinsci/plugins/gogs/GogsWebHook.java | 165 +++++++++--------- 2 files changed, 116 insertions(+), 86 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java index 2a348f3..9683b95 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java @@ -2,6 +2,15 @@ import hudson.model.Item; import jenkins.model.Jenkins; +import org.apache.commons.codec.binary.Hex; + +import javax.annotation.Nonnull; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.Charset; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Collectors; class GogsUtils { @@ -26,4 +35,32 @@ static T find(String jobName, Class type) { return item; } + /** + * Converts Querystring into Map + * + * @param qs Querystring + * @return returns map from querystring + */ + static Map splitQuery(String qs) { + return Pattern.compile("&").splitAsStream(qs) + .map(p -> p.split("=")) + .collect(Collectors.toMap(a -> a[0], a -> a.length > 1 ? a[1] : "")); + } + + /** + * encode sha256 hmac + * + * @param data data to hex + * @param key key of HmacSHA256 + * @return a String with the encoded sha256 hmac + * @throws Exception Something went wrong getting the sha256 hmac + */ + static @Nonnull + String encode(String data, String key) throws Exception { + final Charset asciiCs = Charset.forName("UTF-8"); + final Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); + final SecretKeySpec secret_key = new javax.crypto.spec.SecretKeySpec(asciiCs.encode(key).array(), "HmacSHA256"); + sha256_HMAC.init(secret_key); + return Hex.encodeHexString(sha256_HMAC.doFinal(data.getBytes("UTF-8"))); + } } diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java index 4f56512..5f302a2 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java @@ -30,26 +30,21 @@ associated documentation files (the "Software"), to deal in the Software without import net.sf.json.JSONObject; import org.acegisecurity.context.SecurityContext; import org.acegisecurity.context.SecurityContextHolder; -import org.apache.commons.codec.binary.Hex; import org.apache.commons.io.IOUtils; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; -import javax.annotation.Nonnull; -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.io.PrintWriter; import java.net.URLDecoder; -import java.nio.charset.Charset; -import java.util.Arrays; import java.util.Map; +import java.util.Objects; import java.util.StringJoiner; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; import java.util.regex.Pattern; -import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Strings.isNullOrEmpty; @@ -60,8 +55,12 @@ associated documentation files (the "Software"), to deal in the Software without @Extension public class GogsWebHook implements UnprotectedRootAction { private final static Logger LOGGER = Logger.getLogger(GogsWebHook.class.getName()); - static final String URLNAME = "gogs-webhook"; private static final String DEFAULT_CHARSET = "UTF-8"; + private GogsResults result = new GogsResults(); + private String gogsDelivery = null; + private String gogsSignature = null; + private String jobName = null; + static final String URLNAME = "gogs-webhook"; public String getDisplayName() { return null; @@ -75,21 +74,26 @@ public String getUrlName() { return URLNAME; } - /** - * encode sha256 hmac - * - * @param data data to hex - * @param key key of HmacSHA256 - * @return a String with the encoded sha256 hmac - * @throws Exception Something went wrong getting the sha256 hmac - */ - private static @Nonnull - String encode(String data, String key) throws Exception { - final Charset asciiCs = Charset.forName("UTF-8"); - final Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); - final SecretKeySpec secret_key = new javax.crypto.spec.SecretKeySpec(asciiCs.encode(key).array(), "HmacSHA256"); - sha256_HMAC.init(secret_key); - return Hex.encodeHexString(sha256_HMAC.doFinal(data.getBytes("UTF-8"))); + private String getGogsDelivery() { + if (isNullOrEmpty(gogsDelivery)) { + return "Triggered by Jenkins-Gogs-Plugin. Delivery ID unknown."; + } + return "Gogs-ID: " + gogsDelivery; + } + + private void setGogsDelivery(String gogsDelivery) { + this.gogsDelivery = gogsDelivery; + } + + private String getGogsSignature() { + if (isNullOrEmpty(gogsSignature)) { + gogsSignature = null; + } + return gogsSignature; + } + + private void setGogsSignature(String gogsSignature) { + this.gogsSignature = gogsSignature; } /** @@ -100,52 +104,17 @@ String encode(String data, String key) throws Exception { * @throws IOException problem while parsing */ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException { - GogsResults result = new GogsResults(); GogsPayloadProcessor payloadProcessor = new GogsPayloadProcessor(); - //Check that we have something to process - checkNotNull(req, "Null request submitted to doIndex method"); - checkNotNull(rsp, "Null reply submitted to doIndex method"); - - // Get X-Gogs-Event - String event = req.getHeader("X-Gogs-Event"); - if (!"push".equals(event)) { - result.setStatus(403, "Only push event can be accepted."); - exitWebHook(result, rsp); + if (!sanityChecks(req, rsp)) { return; } // Get X-Gogs-Delivery header with deliveryID - String gogsDelivery = req.getHeader("X-Gogs-Delivery"); - if (isNullOrEmpty(gogsDelivery)) { - gogsDelivery = "Triggered by Jenkins-Gogs-Plugin. Delivery ID unknown."; - } else { - gogsDelivery = "Gogs-ID: " + gogsDelivery; - } + setGogsDelivery(req.getHeader("X-Gogs-Delivery")); // Get X-Gogs-Signature - String gogsSignature = req.getHeader("X-Gogs-Signature"); - if (isNullOrEmpty(gogsSignature)) { - gogsSignature = null; - } - - - // Get queryStringMap from the URI - String queryString = checkNotNull(req.getQueryString(), "The queryString in the request is null"); - Map queryStringMap = checkNotNull(splitQuery(queryString), "Null queryStringMap"); - - //Do we have the job name parameter ? - if (!queryStringMap.containsKey("job")) { - result.setStatus(404, "Parameter 'job' is missing."); - exitWebHook(result, rsp); - return; - } - String jobName = queryStringMap.get("job").toString(); - if (isNullOrEmpty(jobName)) { - result.setStatus(404, "No value assigned to parameter 'job'"); - exitWebHook(result, rsp); - return; - } + setGogsSignature(req.getHeader("X-Gogs-Signature")); // Get the POST stream String body = IOUtils.toString(req.getInputStream(), DEFAULT_CHARSET); @@ -182,15 +151,12 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException .forEach(stringJoiner::add); String ref = stringJoiner.toString(); - Arrays.asList(jobName, jobName + "/" + ref).forEach(j -> { - Job job = GogsUtils.find(j, Job.class); - if (job != null) { - foundJob.set(true); - /* secret is stored in the properties of Job */ - final GogsProjectProperty property = (GogsProjectProperty) job.getProperty(GogsProjectProperty.class); - if (property != null) { /* only if Gogs secret is defined on the job */ - jSecret.set(property.getGogsSecret()); /* Secret provided by Jenkins */ - } + /* secret is stored in the properties of Job */ + Stream.of(jobName, jobName + "/" + ref).map(j -> GogsUtils.find(j, Job.class)).filter(Objects::nonNull).forEach(job -> { + foundJob.set(true); + final GogsProjectProperty property = (GogsProjectProperty) job.getProperty(GogsProjectProperty.class); + if (property != null) { /* only if Gogs secret is defined on the job */ + jSecret.set(property.getGogsSecret()); /* Secret provided by Jenkins */ } }); } finally { @@ -198,11 +164,11 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException } String gSecret = null; - if (gogsSignature == null) { + if (getGogsSignature() == null) { gSecret = jsonObject.optString("secret", null); /* Secret provided by Gogs < 0.10.x */ } else { try { - if (gogsSignature.equals(encode(body, jSecret.get()))) { + if (getGogsSignature().equals(GogsUtils.encode(body, jSecret.get()))) { gSecret = jSecret.get(); // now hex is right, continue to old logic } @@ -217,10 +183,10 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException LOGGER.warning(msg); } else if (isNullOrEmpty(jSecret.get()) && isNullOrEmpty(gSecret)) { /* No password is set in Jenkins and Gogs, run without secrets */ - result = payloadProcessor.triggerJobs(jobName, gogsDelivery); + result = payloadProcessor.triggerJobs(jobName, getGogsDelivery()); } else if (!isNullOrEmpty(jSecret.get()) && jSecret.get().equals(gSecret)) { /* Password is set in Jenkins and Gogs, and is correct */ - result = payloadProcessor.triggerJobs(jobName, gogsDelivery); + result = payloadProcessor.triggerJobs(jobName, getGogsDelivery()); } else { /* Gogs and Jenkins secrets differs */ result.setStatus(403, "Incorrect secret"); @@ -232,6 +198,45 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException exitWebHook(result, rsp); } + /*** + * Do sanity checks + * + * @param req Request + * @param rsp Response + * @throws IOException + */ + private boolean sanityChecks(StaplerRequest req, StaplerResponse rsp) throws IOException { + //Check that we have something to process + checkNotNull(req, "Null request submitted to doIndex method"); + checkNotNull(rsp, "Null reply submitted to doIndex method"); + + // Get X-Gogs-Event + if (!"push".equals(req.getHeader("X-Gogs-Event"))) { + result.setStatus(403, "Only push event can be accepted."); + exitWebHook(result, rsp); + return false; + } + + // Get queryStringMap from the URI + String queryString = checkNotNull(req.getQueryString(), "The queryString in the request is null"); + Map queryStringMap = checkNotNull(GogsUtils.splitQuery(queryString), "Null queryStringMap"); + + //Do we have the job name parameter ? + if (!queryStringMap.containsKey("job")) { + result.setStatus(404, "Parameter 'job' is missing."); + exitWebHook(result, rsp); + return false; + } + + jobName = queryStringMap.get("job").toString(); + if (isNullOrEmpty(jobName)) { + result.setStatus(404, "No value assigned to parameter 'job'"); + exitWebHook(result, rsp); + return false; + } + return true; + } + /** * Exit the WebHook * @@ -250,18 +255,6 @@ private void exitWebHook(GogsResults result, StaplerResponse resp) throws IOExce PrintWriter printer = resp.getWriter(); printer.print(json.toString()); } - - /** - * Converts Querystring into Map - * - * @param qs Querystring - * @return returns map from querystring - */ - private static Map splitQuery(String qs) { - return Pattern.compile("&").splitAsStream(qs) - .map(p -> p.split("=")) - .collect(Collectors.toMap(a -> a[0], a -> a.length > 1 ? a[1] : "")); - } } // vim: set ts=4 sw=4 tw=0 ft=java et : From 1681be447432746c6e642bd0844c56e6607c53ce Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Fri, 27 Apr 2018 22:16:09 +0200 Subject: [PATCH 08/19] More fixes --- pom.xml | 91 +++++++++++++++++-- .../plugins/gogs/GogsPayloadProcessor.java | 7 +- .../plugins/gogs/GogsWebHookTest.java | 12 +-- 3 files changed, 91 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index 1b2f1e6..27f0ec4 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 @@ -12,10 +13,16 @@ hpi - 1.625.3 8 - 2.13 8 + + + 1.625.3 + 2.36 + + + 3.1.2 + false Jenkins Gogs plugin @@ -76,14 +83,7 @@ com.google.guava guava 17.0 - - - - - - - org.apache.httpcomponents fluent-hc @@ -110,6 +110,77 @@ + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.21.0 + + ${jaCoCoArgLine} + + **/IT*.java + + + + + default-test + test + + test + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.1 + + + default-prepare-agent + + prepare-agent + + + jaCoCoArgLine + + + + default-report + + report + + + + default-check + + check + + + + + + org.eluder.coveralls + coveralls-maven-plugin + 4.3.0 + + + org.owasp + dependency-check-maven + ${owasp.version} + + + default-check + + check + + + + + + + withIntegrationTest diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java index f7c5a87..03234a0 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java @@ -26,7 +26,6 @@ associated documentation files (the "Software"), to deal in the Software without import hudson.model.BuildableItem; import hudson.model.Cause; import hudson.security.ACL; -import jenkins.model.ParameterizedJobMixIn; import jenkins.triggers.SCMTriggerItem; import org.acegisecurity.context.SecurityContext; import org.acegisecurity.context.SecurityContextHolder; @@ -39,6 +38,8 @@ associated documentation files (the "Software"), to deal in the Software without import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; +import static jenkins.model.ParameterizedJobMixIn.ParameterizedJob; + class GogsPayloadProcessor { private static final Logger LOGGER = Logger.getLogger(GogsPayloadProcessor.class.getName()); private final Map payload = new HashMap<>(); @@ -60,8 +61,8 @@ public GogsResults triggerJobs(String jobName, String deliveryID) { if (project != null) { Cause cause = new GogsCause(deliveryID); - if (project instanceof ParameterizedJobMixIn.ParameterizedJob) { - ParameterizedJobMixIn.ParameterizedJob pJob = (ParameterizedJobMixIn.ParameterizedJob) project; + if (project instanceof ParameterizedJob) { + ParameterizedJob pJob = (ParameterizedJob) project; pJob.getTriggers().values().stream() .filter(trigger1 -> trigger1 instanceof GogsTrigger).findFirst() .ifPresent((g) -> { diff --git a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java index 13ff144..f091608 100644 --- a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java +++ b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java @@ -104,7 +104,7 @@ public void whenWrongHeaderTypeMustReturnError() throws Exception { StaplerResponse staplerResponse = Mockito.mock(ResponseImpl.class); when(staplerRequest.getHeader("X-Gogs-Event")).thenReturn("junk"); - //perform the testÎ + //perform the test performDoIndexTest(staplerRequest, staplerResponse, uniqueFile); //validate that everything was done as planed @@ -149,7 +149,7 @@ public void whenNoJobInQueryStringMustReturnError() throws Exception { when(staplerRequest.getHeader("X-Gogs-Event")).thenReturn("push"); when(staplerRequest.getQueryString()).thenReturn("foo=bar&blaah=blaah"); - //perform the testÎ + //perform the test performDoIndexTest(staplerRequest, staplerResponse, uniqueFile); //validate that everything was done as planed @@ -171,7 +171,7 @@ public void whenEmptyJobInQueryStringMustReturnError() throws Exception { when(staplerRequest.getHeader("X-Gogs-Event")).thenReturn("push"); when(staplerRequest.getQueryString()).thenReturn("job&foo=bar"); - //perform the testÎ + //perform the test performDoIndexTest(staplerRequest, staplerResponse, uniqueFile); //validate that everything was done as planed @@ -193,7 +193,7 @@ public void whenEmptyJob2InQueryStringMustReturnError() throws Exception { when(staplerRequest.getHeader("X-Gogs-Event")).thenReturn("push"); when(staplerRequest.getQueryString()).thenReturn("job=&foo=bar"); - //perform the testÎ + //perform the test performDoIndexTest(staplerRequest, staplerResponse, uniqueFile); //validate that everything was done as planed @@ -220,7 +220,7 @@ public void whenUriDoesNotContainUrlNameMustReturnError() throws Exception { when(staplerRequest.getInputStream()).thenReturn(inputStream); when(staplerRequest.getRequestURI()).thenReturn("/badUri/aaa"); - //perform the testÎ + //perform the test performDoIndexTest(staplerRequest, staplerResponse, uniqueFile); //validate that everything was done as planed @@ -284,4 +284,4 @@ public void setReadListener(ReadListener readListener) { } } -} +} \ No newline at end of file From 99bdb2c0699b5d34085fffaf94647ec2e47fde94 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Wed, 9 May 2018 20:31:19 +0200 Subject: [PATCH 09/19] Added jackson --- pom.xml | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 27f0ec4..78b07a1 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,6 @@ plugin 2.11 - org.jenkins-ci.plugins gogs-webhook 1.0.15-SNAPSHOT hpi @@ -23,6 +22,9 @@ 3.1.2 false + + + 2.9.5 Jenkins Gogs plugin @@ -55,18 +57,28 @@ org.jenkins-ci.plugins git - 2.2.5 + 3.8.0 org.apache.httpcomponents httpclient - 4.5.1 + 4.5.5 commons-codec commons-codec 1.10 + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + junit junit @@ -108,6 +120,18 @@ 6.3 test + + com.fasterxml.jackson.core + jackson-databind + 2.8.3 + compile + + + com.fasterxml.jackson.core + jackson-databind + 2.8.3 + compile + From f93aff1a3f1c780213c7f809fd3b8d5a1604a83b Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Wed, 9 May 2018 20:31:52 +0200 Subject: [PATCH 10/19] Payload json now in pojo and used in cause --- pom.xml | 16 +---- .../org/jenkinsci/plugins/gogs/GogsCause.java | 63 +++++++++++++++++- .../gogs/GogsEnvironmentContributor.java | 24 +++++++ .../jenkinsci/plugins/gogs/GogsPayload.java | 12 +++- .../plugins/gogs/GogsPayloadData.java | 66 +++++++++++++++++++ .../plugins/gogs/GogsPayloadProcessor.java | 14 ++-- .../jenkinsci/plugins/gogs/GogsWebHook.java | 10 +-- 7 files changed, 176 insertions(+), 29 deletions(-) create mode 100644 src/main/java/org/jenkinsci/plugins/gogs/GogsEnvironmentContributor.java create mode 100644 src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadData.java diff --git a/pom.xml b/pom.xml index 78b07a1..715eeca 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ 8 - 1.625.3 + 2.60.1 2.36 @@ -94,7 +94,7 @@ com.google.guava guava - 17.0 + 25.0-jre org.apache.httpcomponents @@ -120,18 +120,6 @@ 6.3 test - - com.fasterxml.jackson.core - jackson-databind - 2.8.3 - compile - - - com.fasterxml.jackson.core - jackson-databind - 2.8.3 - compile - diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsCause.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsCause.java index a00583e..d1ef47d 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsCause.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsCause.java @@ -23,15 +23,76 @@ associated documentation files (the "Software"), to deal in the Software without package org.jenkinsci.plugins.gogs; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import hudson.model.Cause; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + class GogsCause extends Cause { - private final String deliveryID; + private String deliveryID; + private GogsPayloadData gogsPayloadData; + private Map envVars = new HashMap<>(); + private final static Logger LOGGER = Logger.getLogger(GogsCause.class.getName()); + + + public GogsCause() { + } public GogsCause(String deliveryID) { this.deliveryID = deliveryID; } + public String getDeliveryID() { + return deliveryID; + } + + public Map getEnvVars() { + return envVars; + } + + public void setDeliveryID(String deliveryID) { + this.deliveryID = deliveryID; + } + + public void setGogsPayloadData(String json) { + Map map = null; + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + try { + gogsPayloadData = objectMapper.readValue(json, GogsPayloadData.class); + map = objectMapper.convertValue(gogsPayloadData, new TypeReference>() { + }); + } catch (Exception e) { + LOGGER.severe(e.getMessage()); + } + if (gogsPayloadData != null) { + iterate(map, null); + } + + } + + private void iterate(Map map, String prefix) { + for (Map.Entry entry : map.entrySet()) { + StringBuilder env_name = new StringBuilder(); + if (prefix != null) + env_name.append(prefix.toUpperCase()).append("_"); + + if (entry.getValue() instanceof Map) { + iterate((Map) entry.getValue(), env_name + entry.getKey().toUpperCase()); + } else if (entry.getValue() instanceof String || entry.getValue() instanceof Long || entry.getValue() instanceof Boolean) { + env_name.append(entry.getKey().toUpperCase()); + envVars.put("GOGS_" + env_name.toString(), entry.getValue().toString()); + } + } + } + @Override public String getShortDescription() { return this.deliveryID; diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsEnvironmentContributor.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsEnvironmentContributor.java new file mode 100644 index 0000000..c134506 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsEnvironmentContributor.java @@ -0,0 +1,24 @@ +package org.jenkinsci.plugins.gogs; + +import hudson.EnvVars; +import hudson.Extension; +import hudson.model.Run; +import hudson.model.TaskListener; +import jenkins.model.CoreEnvironmentContributor; + +import javax.annotation.Nonnull; +import java.io.IOException; + +@Extension +public class GogsEnvironmentContributor extends CoreEnvironmentContributor { + @Override + public void buildEnvironmentFor(@Nonnull Run r, @Nonnull EnvVars envs, @Nonnull TaskListener listener) + throws IOException, InterruptedException { + GogsCause gogsCause; + + gogsCause = (GogsCause) r.getCause(GogsCause.class); + if (gogsCause != null) { + envs.putAll(gogsCause.getEnvVars()); + } + } +} diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayload.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayload.java index 95e2b9f..44c08e4 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayload.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayload.java @@ -11,21 +11,27 @@ import java.util.logging.Logger; class GogsPayload extends InvisibleAction implements EnvironmentContributingAction { - private final Map payload; + private Map payload; + private GogsCause gogsCause; public GogsPayload(Map payload) { this.payload = payload; } + public GogsPayload(GogsCause gogsCause) { + this.gogsCause = gogsCause; + } + @Nonnull - private Map getPayload() { + public Map getPayload() { return payload; } @Override public void buildEnvVars(AbstractBuild abstractBuild, EnvVars envVars) { LOGGER.log(Level.FINEST, "Injecting GOGS_PAYLOAD: {0}", getPayload()); - payload.forEach((key, value) -> envVars.put("GOGS_" + key.toUpperCase(), value)); +// payload.forEach((key, value) -> envVars.put("GOGS_" + key.toUpperCase(), value)); + envVars.putAll(gogsCause.getEnvVars()); } private static final Logger LOGGER = Logger.getLogger(GogsPayload.class.getName()); diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadData.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadData.java new file mode 100644 index 0000000..c470f8f --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadData.java @@ -0,0 +1,66 @@ +package org.jenkinsci.plugins.gogs; + +import java.util.List; + +public class GogsPayloadData { + public static class UserDetails { + public String name; + public String email; + public String username; + } + + public static class Owner { + public Long id; + public String login; + public String full_name; + public String email; + public String avatar_url; + public String username; + } + + public static class Commits { + public String id; + public String message; + public String url; + public UserDetails author; + public UserDetails committer; + public List added; + public List removed; + public List modified; + public String timestamp; + } + + public static class Repository { + public Long id; + public Owner owner; + public String name; + public String full_name; + public String description; + public Boolean Private; + public Boolean fork; + public Boolean parent; + public Boolean empty; + public Boolean mirror; + public Long size; + public String html_url; + public String ssh_url; + public String clone_url; + public String website; + public Long stars_count; + public Long forks_count; + public Long watchers_count; + public Long open_issues_count; + public String default_branch; + public String created_at; + public String updated_at; + } + + public String ref; + public String before; + public String after; + public String compare_url; + public List commits; + public Repository repository; + public Owner pusher; + public Owner sender; +} diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java index 03234a0..db1a5ba 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java @@ -24,7 +24,6 @@ associated documentation files (the "Software"), to deal in the Software without package org.jenkinsci.plugins.gogs; import hudson.model.BuildableItem; -import hudson.model.Cause; import hudson.security.ACL; import jenkins.triggers.SCMTriggerItem; import org.acegisecurity.context.SecurityContext; @@ -43,15 +42,16 @@ associated documentation files (the "Software"), to deal in the Software without class GogsPayloadProcessor { private static final Logger LOGGER = Logger.getLogger(GogsPayloadProcessor.class.getName()); private final Map payload = new HashMap<>(); + private GogsCause gogsCause = new GogsCause(); GogsPayloadProcessor() { } - public void setPayload(String k, String v) { - this.payload.put(k, v); + public void setCause(GogsCause gogsCause) { + this.gogsCause = gogsCause; } - public GogsResults triggerJobs(String jobName, String deliveryID) { + public GogsResults triggerJobs(String jobName) { AtomicBoolean jobdone = new AtomicBoolean(false); SecurityContext saveCtx = ACL.impersonate(ACL.SYSTEM); GogsResults result = new GogsResults(); @@ -59,14 +59,14 @@ public GogsResults triggerJobs(String jobName, String deliveryID) { try { BuildableItem project = GogsUtils.find(jobName, BuildableItem.class); if (project != null) { - Cause cause = new GogsCause(deliveryID); if (project instanceof ParameterizedJob) { ParameterizedJob pJob = (ParameterizedJob) project; pJob.getTriggers().values().stream() .filter(trigger1 -> trigger1 instanceof GogsTrigger).findFirst() .ifPresent((g) -> { - GogsPayload gogsPayload = new GogsPayload(this.payload); +// GogsPayload gogsPayload = new GogsPayload(this.payload); + GogsPayload gogsPayload = new GogsPayload(this.gogsCause); Optional.ofNullable(SCMTriggerItem.SCMTriggerItems.asSCMTriggerItem(project)) .ifPresent((item) -> { item.scheduleBuild2(0, gogsPayload); @@ -75,7 +75,7 @@ public GogsResults triggerJobs(String jobName, String deliveryID) { }); } if (!jobdone.get()) { - project.scheduleBuild(0, cause); + project.scheduleBuild(0, gogsCause); jobdone.set(true); } } diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java index 5f302a2..cf2985d 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java @@ -105,6 +105,7 @@ private void setGogsSignature(String gogsSignature) { */ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException { GogsPayloadProcessor payloadProcessor = new GogsPayloadProcessor(); + GogsCause gogsCause = new GogsCause(); if (!sanityChecks(req, rsp)) { return; @@ -140,8 +141,9 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException AtomicReference jSecret = new AtomicReference<>(null); AtomicBoolean foundJob = new AtomicBoolean(false); - payloadProcessor.setPayload("ref", jsonObject.getString("ref")); - payloadProcessor.setPayload("before", jsonObject.getString("before")); + gogsCause.setGogsPayloadData(jsonObject.toString()); + gogsCause.setDeliveryID(getGogsDelivery()); + payloadProcessor.setCause(gogsCause); SecurityContext saveCtx = ACL.impersonate(ACL.SYSTEM); @@ -183,10 +185,10 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException LOGGER.warning(msg); } else if (isNullOrEmpty(jSecret.get()) && isNullOrEmpty(gSecret)) { /* No password is set in Jenkins and Gogs, run without secrets */ - result = payloadProcessor.triggerJobs(jobName, getGogsDelivery()); + result = payloadProcessor.triggerJobs(jobName); } else if (!isNullOrEmpty(jSecret.get()) && jSecret.get().equals(gSecret)) { /* Password is set in Jenkins and Gogs, and is correct */ - result = payloadProcessor.triggerJobs(jobName, getGogsDelivery()); + result = payloadProcessor.triggerJobs(jobName); } else { /* Gogs and Jenkins secrets differs */ result.setStatus(403, "Incorrect secret"); From 66256e67c6c0560f993d3093d7fe08fcede24f72 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Thu, 10 May 2018 01:35:05 +0200 Subject: [PATCH 11/19] Code cleanup --- .../org/jenkinsci/plugins/gogs/GogsCause.java | 12 ++++-------- .../gogs/GogsEnvironmentContributor.java | 5 ++--- .../org/jenkinsci/plugins/gogs/GogsPayload.java | 17 ++--------------- .../jenkinsci/plugins/gogs/GogsPayloadData.java | 5 +++++ .../plugins/gogs/GogsPayloadProcessor.java | 10 ++-------- .../org/jenkinsci/plugins/gogs/GogsUtils.java | 2 +- .../org/jenkinsci/plugins/gogs/GogsWebHook.java | 11 +++-------- .../plugins/gogs/GogsConfigHandler.java | 5 ++--- 8 files changed, 21 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsCause.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsCause.java index d1ef47d..4e5ba90 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsCause.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsCause.java @@ -34,8 +34,7 @@ associated documentation files (the "Software"), to deal in the Software without class GogsCause extends Cause { private String deliveryID; - private GogsPayloadData gogsPayloadData; - private Map envVars = new HashMap<>(); + private final Map envVars = new HashMap<>(); private final static Logger LOGGER = Logger.getLogger(GogsCause.class.getName()); @@ -46,10 +45,6 @@ public GogsCause(String deliveryID) { this.deliveryID = deliveryID; } - public String getDeliveryID() { - return deliveryID; - } - public Map getEnvVars() { return envVars; } @@ -66,13 +61,13 @@ public void setGogsPayloadData(String json) { objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); try { - gogsPayloadData = objectMapper.readValue(json, GogsPayloadData.class); + GogsPayloadData gogsPayloadData = objectMapper.readValue(json, GogsPayloadData.class); map = objectMapper.convertValue(gogsPayloadData, new TypeReference>() { }); } catch (Exception e) { LOGGER.severe(e.getMessage()); } - if (gogsPayloadData != null) { + if (map != null) { iterate(map, null); } @@ -85,6 +80,7 @@ private void iterate(Map map, String prefix) { env_name.append(prefix.toUpperCase()).append("_"); if (entry.getValue() instanceof Map) { + //noinspection unchecked iterate((Map) entry.getValue(), env_name + entry.getKey().toUpperCase()); } else if (entry.getValue() instanceof String || entry.getValue() instanceof Long || entry.getValue() instanceof Boolean) { env_name.append(entry.getKey().toUpperCase()); diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsEnvironmentContributor.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsEnvironmentContributor.java index c134506..d84d481 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsEnvironmentContributor.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsEnvironmentContributor.java @@ -7,13 +7,12 @@ import jenkins.model.CoreEnvironmentContributor; import javax.annotation.Nonnull; -import java.io.IOException; +@SuppressWarnings("unused") @Extension public class GogsEnvironmentContributor extends CoreEnvironmentContributor { @Override - public void buildEnvironmentFor(@Nonnull Run r, @Nonnull EnvVars envs, @Nonnull TaskListener listener) - throws IOException, InterruptedException { + public void buildEnvironmentFor(@Nonnull Run r, @Nonnull EnvVars envs, @Nonnull TaskListener listener) { GogsCause gogsCause; gogsCause = (GogsCause) r.getCause(GogsCause.class); diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayload.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayload.java index 44c08e4..d34b467 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayload.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayload.java @@ -5,32 +5,19 @@ import hudson.model.EnvironmentContributingAction; import hudson.model.InvisibleAction; -import javax.annotation.Nonnull; -import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; class GogsPayload extends InvisibleAction implements EnvironmentContributingAction { - private Map payload; - private GogsCause gogsCause; - - public GogsPayload(Map payload) { - this.payload = payload; - } + private final GogsCause gogsCause; public GogsPayload(GogsCause gogsCause) { this.gogsCause = gogsCause; } - @Nonnull - public Map getPayload() { - return payload; - } - @Override public void buildEnvVars(AbstractBuild abstractBuild, EnvVars envVars) { - LOGGER.log(Level.FINEST, "Injecting GOGS_PAYLOAD: {0}", getPayload()); -// payload.forEach((key, value) -> envVars.put("GOGS_" + key.toUpperCase(), value)); + LOGGER.log(Level.FINEST, "Injecting GOGS_PAYLOAD: {0}", gogsCause.getEnvVars()); envVars.putAll(gogsCause.getEnvVars()); } diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadData.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadData.java index c470f8f..c730882 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadData.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadData.java @@ -2,13 +2,16 @@ import java.util.List; +@SuppressWarnings("unused") public class GogsPayloadData { + @SuppressWarnings("unused") public static class UserDetails { public String name; public String email; public String username; } + @SuppressWarnings("unused") public static class Owner { public Long id; public String login; @@ -18,6 +21,7 @@ public static class Owner { public String username; } + @SuppressWarnings("unused") public static class Commits { public String id; public String message; @@ -30,6 +34,7 @@ public static class Commits { public String timestamp; } + @SuppressWarnings("unused") public static class Repository { public Long id; public Owner owner; diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java index db1a5ba..fb99ed3 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadProcessor.java @@ -25,14 +25,11 @@ associated documentation files (the "Software"), to deal in the Software without import hudson.model.BuildableItem; import hudson.security.ACL; +import hudson.security.ACLContext; import jenkins.triggers.SCMTriggerItem; -import org.acegisecurity.context.SecurityContext; -import org.acegisecurity.context.SecurityContextHolder; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.HashMap; -import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; @@ -41,7 +38,6 @@ associated documentation files (the "Software"), to deal in the Software without class GogsPayloadProcessor { private static final Logger LOGGER = Logger.getLogger(GogsPayloadProcessor.class.getName()); - private final Map payload = new HashMap<>(); private GogsCause gogsCause = new GogsCause(); GogsPayloadProcessor() { @@ -53,10 +49,9 @@ public void setCause(GogsCause gogsCause) { public GogsResults triggerJobs(String jobName) { AtomicBoolean jobdone = new AtomicBoolean(false); - SecurityContext saveCtx = ACL.impersonate(ACL.SYSTEM); GogsResults result = new GogsResults(); - try { + try (ACLContext ctx = ACL.as(ACL.SYSTEM)) { BuildableItem project = GogsUtils.find(jobName, BuildableItem.class); if (project != null) { @@ -92,7 +87,6 @@ public GogsResults triggerJobs(String jobName) { result.setStatus(404, msg); LOGGER.warning(msg); } - SecurityContextHolder.setContext(saveCtx); } return result; diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java index 9683b95..86c5280 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java @@ -24,7 +24,7 @@ private GogsUtils() { * @return the Job matching the given name, or {@code null} when not found */ static T find(String jobName, Class type) { - Jenkins jenkins = Jenkins.getActiveInstance(); + Jenkins jenkins = Jenkins.getInstance(); // direct search, can be used to find folder based items // T item = jenkins.getItemByFullName(jobName, type); if (item == null) { diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java index cf2985d..e993e09 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java @@ -27,9 +27,8 @@ associated documentation files (the "Software"), to deal in the Software without import hudson.model.Job; import hudson.model.UnprotectedRootAction; import hudson.security.ACL; +import hudson.security.ACLContext; import net.sf.json.JSONObject; -import org.acegisecurity.context.SecurityContext; -import org.acegisecurity.context.SecurityContextHolder; import org.apache.commons.io.IOUtils; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -145,9 +144,7 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException gogsCause.setDeliveryID(getGogsDelivery()); payloadProcessor.setCause(gogsCause); - SecurityContext saveCtx = ACL.impersonate(ACL.SYSTEM); - - try { + try (ACLContext ctx = ACL.as(ACL.SYSTEM)) { StringJoiner stringJoiner = new StringJoiner("%2F"); Pattern.compile("/").splitAsStream((String) jsonObject.get("ref")).skip(2) .forEach(stringJoiner::add); @@ -161,8 +158,6 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException jSecret.set(property.getGogsSecret()); /* Secret provided by Jenkins */ } }); - } finally { - SecurityContextHolder.setContext(saveCtx); } String gSecret = null; @@ -205,7 +200,7 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException * * @param req Request * @param rsp Response - * @throws IOException + * @throws IOException Exception */ private boolean sanityChecks(StaplerRequest req, StaplerResponse rsp) throws IOException { //Check that we have something to process diff --git a/src/test/java/org/jenkinsci/plugins/gogs/GogsConfigHandler.java b/src/test/java/org/jenkinsci/plugins/gogs/GogsConfigHandler.java index 7c520e0..260b368 100644 --- a/src/test/java/org/jenkinsci/plugins/gogs/GogsConfigHandler.java +++ b/src/test/java/org/jenkinsci/plugins/gogs/GogsConfigHandler.java @@ -104,10 +104,9 @@ int createWebHook(String jsonCommand, String projectName) throws IOException { String result = executor .execute(Request.Post(gogsHooksConfigUrl).bodyString(jsonCommand, ContentType.APPLICATION_JSON)) .returnContent().asString(); - JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON( result ); - int id = jsonObject.getInt("id"); + JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON(result); - return id; + return jsonObject.getInt("id"); } /** From c49bd6c8517654966b5d0154e1e1887437b0d247 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Thu, 10 May 2018 02:11:31 +0200 Subject: [PATCH 12/19] Fix injectTest issue --- pom.xml | 29 +++++++- .../org/jenkinsci/plugins/gogs/GogsCause.java | 14 ++-- .../plugins/gogs/GogsPayloadData.java | 71 ------------------- .../plugins/gogs/GogsProjectProperty.java | 3 +- .../org/jenkinsci/plugins/gogs/GogsUtils.java | 16 ++++- 5 files changed, 48 insertions(+), 85 deletions(-) delete mode 100644 src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadData.java diff --git a/pom.xml b/pom.xml index 715eeca..5480ba0 100644 --- a/pom.xml +++ b/pom.xml @@ -16,8 +16,8 @@ 8 - 2.60.1 - 2.36 + 2.60.3 + 2.38 3.1.2 @@ -120,6 +120,12 @@ 6.3 test + + org.jenkins-ci.plugins + display-url-api + 2.2.0 + test + @@ -169,6 +175,25 @@ check + + + + CLASS + + + LINE + COVEREDRATIO + 0.0 + + + BRANCH + COVEREDRATIO + 0.0 + + + + + diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsCause.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsCause.java index 4e5ba90..a85c101 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsCause.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsCause.java @@ -23,7 +23,6 @@ associated documentation files (the "Software"), to deal in the Software without package org.jenkinsci.plugins.gogs; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import hudson.model.Cause; @@ -32,6 +31,8 @@ associated documentation files (the "Software"), to deal in the Software without import java.util.Map; import java.util.logging.Logger; +import static org.jenkinsci.plugins.gogs.GogsUtils.cast; + class GogsCause extends Cause { private String deliveryID; private final Map envVars = new HashMap<>(); @@ -54,23 +55,20 @@ public void setDeliveryID(String deliveryID) { } public void setGogsPayloadData(String json) { - Map map = null; + Map gogsPayloadData = null; ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); try { - GogsPayloadData gogsPayloadData = objectMapper.readValue(json, GogsPayloadData.class); - map = objectMapper.convertValue(gogsPayloadData, new TypeReference>() { - }); + gogsPayloadData = cast(objectMapper.readValue(json, Map.class)); } catch (Exception e) { LOGGER.severe(e.getMessage()); } - if (map != null) { - iterate(map, null); + if (gogsPayloadData != null) { + iterate(gogsPayloadData, null); } - } private void iterate(Map map, String prefix) { diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadData.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadData.java deleted file mode 100644 index c730882..0000000 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsPayloadData.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.jenkinsci.plugins.gogs; - -import java.util.List; - -@SuppressWarnings("unused") -public class GogsPayloadData { - @SuppressWarnings("unused") - public static class UserDetails { - public String name; - public String email; - public String username; - } - - @SuppressWarnings("unused") - public static class Owner { - public Long id; - public String login; - public String full_name; - public String email; - public String avatar_url; - public String username; - } - - @SuppressWarnings("unused") - public static class Commits { - public String id; - public String message; - public String url; - public UserDetails author; - public UserDetails committer; - public List added; - public List removed; - public List modified; - public String timestamp; - } - - @SuppressWarnings("unused") - public static class Repository { - public Long id; - public Owner owner; - public String name; - public String full_name; - public String description; - public Boolean Private; - public Boolean fork; - public Boolean parent; - public Boolean empty; - public Boolean mirror; - public Long size; - public String html_url; - public String ssh_url; - public String clone_url; - public String website; - public Long stars_count; - public Long forks_count; - public Long watchers_count; - public Long open_issues_count; - public String default_branch; - public String created_at; - public String updated_at; - } - - public String ref; - public String before; - public String after; - public String compare_url; - public List commits; - public Repository repository; - public Owner pusher; - public Owner sender; -} diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsProjectProperty.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsProjectProperty.java index 79f4e11..9b0a93d 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsProjectProperty.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsProjectProperty.java @@ -31,6 +31,7 @@ associated documentation files (the "Software"), to deal in the Software without import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; +import javax.annotation.Nonnull; import java.util.logging.Logger; @SuppressWarnings("ALL") @@ -68,7 +69,7 @@ public boolean getGogsUsePayload() { return gogsUsePayload; } - public JobProperty newInstance(StaplerRequest req, JSONObject formData) { + public JobProperty newInstance(@Nonnull StaplerRequest req, @Nonnull JSONObject formData) { GogsProjectProperty tpp = req.bindJSON( GogsProjectProperty.class, formData.getJSONObject(GOGS_PROJECT_BLOCK_NAME) diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java index 86c5280..49e9866 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java @@ -4,7 +4,6 @@ import jenkins.model.Jenkins; import org.apache.commons.codec.binary.Hex; -import javax.annotation.Nonnull; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.Charset; @@ -55,12 +54,23 @@ static Map splitQuery(String qs) { * @return a String with the encoded sha256 hmac * @throws Exception Something went wrong getting the sha256 hmac */ - static @Nonnull - String encode(String data, String key) throws Exception { + static String encode(String data, String key) throws Exception { final Charset asciiCs = Charset.forName("UTF-8"); final Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); final SecretKeySpec secret_key = new javax.crypto.spec.SecretKeySpec(asciiCs.encode(key).array(), "HmacSHA256"); sha256_HMAC.init(secret_key); return Hex.encodeHexString(sha256_HMAC.doFinal(data.getBytes("UTF-8"))); } + + /** + * Cast object + * + * @param obj object to cast + * @param cast to type + * @return Casted object + */ + @SuppressWarnings("unchecked") + static public T cast(Object obj) { + return (T) obj; + } } From 112f1dbdefb38a587d8f0ebaaf452c761d8fbb6c Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Sat, 24 Nov 2018 16:07:07 +0100 Subject: [PATCH 13/19] Remove OWASP and run IT headless --- pom.xml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 5480ba0..c091cc3 100644 --- a/pom.xml +++ b/pom.xml @@ -139,6 +139,9 @@ **/IT*.java + + true + @@ -202,19 +205,6 @@ coveralls-maven-plugin 4.3.0 - - org.owasp - dependency-check-maven - ${owasp.version} - - - default-check - - check - - - - From bb3b0b11bc888e138ba5a5965a2ac8f9da22eecf Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Sat, 24 Nov 2018 16:37:24 +0100 Subject: [PATCH 14/19] Bump jackson version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c091cc3..48ebb02 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ false - 2.9.5 + 2.9.7 Jenkins Gogs plugin From 178daa8eb006679fed27eb4a1c6350e4b6caf512 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Sun, 25 Nov 2018 01:17:33 +0100 Subject: [PATCH 15/19] Add branch filter --- pom.xml | 4 +-- .../plugins/gogs/GogsProjectProperty.java | 30 +++++++++++++++++-- .../jenkinsci/plugins/gogs/GogsWebHook.java | 19 ++++++++---- .../gogs/GogsProjectProperty/config.jelly | 9 ++++-- .../plugins/gogs/GogsWebHookTest.java | 27 +++++++++++++++-- 5 files changed, 74 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index 48ebb02..743b819 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.plugins plugin - 2.11 + 2.30 gogs-webhook 1.0.15-SNAPSHOT @@ -16,7 +16,7 @@ 8 - 2.60.3 + 2.138.3 2.38 diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsProjectProperty.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsProjectProperty.java index 9b0a93d..b32f4de 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsProjectProperty.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsProjectProperty.java @@ -36,13 +36,17 @@ associated documentation files (the "Software"), to deal in the Software without @SuppressWarnings("ALL") public class GogsProjectProperty extends JobProperty> { + private static final Logger LOGGER = Logger.getLogger(GogsWebHook.class.getName()); + private final String gogsSecret; private final boolean gogsUsePayload; + private final String gogsBranchFilter; @DataBoundConstructor - public GogsProjectProperty(String gogsSecret, boolean gogsUsePayload) { + public GogsProjectProperty(String gogsSecret, boolean gogsUsePayload, String gogsBranchFilter) { this.gogsSecret = gogsSecret; this.gogsUsePayload = gogsUsePayload; + this.gogsBranchFilter = gogsBranchFilter; } public String getGogsSecret() { @@ -53,13 +57,27 @@ public boolean getGogsUsePayload() { return this.gogsUsePayload; } - private static final Logger LOGGER = Logger.getLogger(GogsWebHook.class.getName()); + public String getGogsBranchFilter() { + return gogsBranchFilter; + } + + public boolean getHasBranchFilter() { + return gogsBranchFilter != null && gogsBranchFilter.length() > 0; + } + + public boolean filterBranch(String ref) { + if (gogsBranchFilter != null && gogsBranchFilter.length() > 0 && !gogsBranchFilter.equals("*")) { + return ref == null || ref.length() == 0 || ref.endsWith(gogsBranchFilter); + } + return true; + } @Extension public static final class DescriptorImpl extends JobPropertyDescriptor { public static final String GOGS_PROJECT_BLOCK_NAME = "gogsProject"; private String gogsSecret; private boolean gogsUsePayload; + private String gogsBranchFilter; public String getGogsSecret() { return gogsSecret; @@ -69,6 +87,10 @@ public boolean getGogsUsePayload() { return gogsUsePayload; } + public String getGogsBranchFilter() { + return gogsBranchFilter; + } + public JobProperty newInstance(@Nonnull StaplerRequest req, @Nonnull JSONObject formData) { GogsProjectProperty tpp = req.bindJSON( GogsProjectProperty.class, @@ -77,15 +99,17 @@ public JobProperty newInstance(@Nonnull StaplerRequest req, @Nonnull JSONObje if (tpp != null) { LOGGER.finest(formData.toString()); LOGGER.finest(tpp.gogsSecret); + LOGGER.finest(tpp.gogsBranchFilter); gogsSecret = tpp.gogsSecret; + gogsBranchFilter = tpp.gogsBranchFilter; } return tpp; } @Override public String getDisplayName() { - return "Gogs Secret"; + return "Gogs Project Property"; } } } diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java index e993e09..98ff3bd 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java @@ -102,6 +102,7 @@ private void setGogsSignature(String gogsSignature) { * @param rsp response * @throws IOException problem while parsing */ + @SuppressWarnings("WeakerAccess") public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException { GogsPayloadProcessor payloadProcessor = new GogsPayloadProcessor(); GogsCause gogsCause = new GogsCause(); @@ -121,7 +122,8 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException if (!body.isEmpty() && req.getRequestURI().contains("/" + URLNAME + "/")) { JSONObject jsonObject = JSONObject.fromObject(body); JSONObject commits = (JSONObject) jsonObject.getJSONArray("commits").get(0); - String message = (String) commits.get("message"); + String ref = jsonObject.getString("ref"); + String message = commits.getString("message"); if (message.startsWith("[IGNORE]")) { // Ignore commits starting with message "[IGNORE]" @@ -140,22 +142,23 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException AtomicReference jSecret = new AtomicReference<>(null); AtomicBoolean foundJob = new AtomicBoolean(false); + AtomicBoolean isRefMatched = new AtomicBoolean(true); gogsCause.setGogsPayloadData(jsonObject.toString()); gogsCause.setDeliveryID(getGogsDelivery()); payloadProcessor.setCause(gogsCause); - try (ACLContext ctx = ACL.as(ACL.SYSTEM)) { + try (ACLContext ignored = ACL.as(ACL.SYSTEM)) { StringJoiner stringJoiner = new StringJoiner("%2F"); - Pattern.compile("/").splitAsStream((String) jsonObject.get("ref")).skip(2) - .forEach(stringJoiner::add); - String ref = stringJoiner.toString(); + Pattern.compile("/").splitAsStream(jsonObject.getString("ref")).skip(2).forEach(stringJoiner::add); + String ref_strj = stringJoiner.toString(); /* secret is stored in the properties of Job */ - Stream.of(jobName, jobName + "/" + ref).map(j -> GogsUtils.find(j, Job.class)).filter(Objects::nonNull).forEach(job -> { + Stream.of(jobName, jobName + "/" + ref_strj).map(j -> GogsUtils.find(j, Job.class)).filter(Objects::nonNull).forEach(job -> { foundJob.set(true); final GogsProjectProperty property = (GogsProjectProperty) job.getProperty(GogsProjectProperty.class); if (property != null) { /* only if Gogs secret is defined on the job */ jSecret.set(property.getGogsSecret()); /* Secret provided by Jenkins */ + isRefMatched.set(property.filterBranch(ref)); } }); } @@ -178,6 +181,10 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException String msg = String.format("Job '%s' is not defined in Jenkins", jobName); result.setStatus(404, msg); LOGGER.warning(msg); + } else if (!isRefMatched.get()) { + String msg = String.format("received ref ('%s') is not matched with branch filter in job '%s'", ref, jobName); + result.setStatus(200, msg); + LOGGER.info(msg); } else if (isNullOrEmpty(jSecret.get()) && isNullOrEmpty(gSecret)) { /* No password is set in Jenkins and Gogs, run without secrets */ result = payloadProcessor.triggerJobs(jobName); diff --git a/src/main/resources/org/jenkinsci/plugins/gogs/GogsProjectProperty/config.jelly b/src/main/resources/org/jenkinsci/plugins/gogs/GogsProjectProperty/config.jelly index 7a90fcd..b8f4314 100644 --- a/src/main/resources/org/jenkinsci/plugins/gogs/GogsProjectProperty/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/gogs/GogsProjectProperty/config.jelly @@ -1,9 +1,14 @@ - + - + + + + + + diff --git a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java index f091608..d8334cc 100644 --- a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java +++ b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java @@ -24,8 +24,7 @@ import java.io.InputStream; import java.io.PrintWriter; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -232,6 +231,30 @@ public void whenUriDoesNotContainUrlNameMustReturnError() throws Exception { log.info("Test succeeded."); } + @Test + public void whenJobBranchNotMatchMustReturnError() throws Exception { + Object[][] test_vals = { + {null, "master", true}, + {null, "dev", true}, + {"", "master", true}, + {"", "dev", true}, + {"*", "master", true}, + {"*", "dev", true}, + {"dev", "master", false}, + {"dev", "dev", true}, + {"master", "master", true}, + {"master", "dev", false}, + }; + for (Object[] test_val : test_vals) { + String filter = (String) test_val[0]; + String ref = (String) test_val[1]; + boolean ret = (Boolean) test_val[2]; + GogsProjectProperty property = new GogsProjectProperty(null, false, filter); + assertSame(String.format("branch filter check failed for [%s -> %s]", ref, filter), ret, property.filterBranch(ref)); + } + log.info("Test succeeded."); + } + // // Helper methods // From 633230454bfb15330cf4e8ea47be36ff9313e50f Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Sat, 20 Jul 2019 22:06:52 +0200 Subject: [PATCH 16/19] Added latest master changes and release feature --- Jenkinsfile | 2 +- pom.xml | 14 +- .../plugins/gogs/GogsProjectProperty.java | 37 ++-- .../org/jenkinsci/plugins/gogs/GogsUtils.java | 28 ++- .../jenkinsci/plugins/gogs/GogsWebHook.java | 65 ++++-- .../docker/gitserver-dockerfile/setup-gogs.sh | 7 +- src/test/docker/jenkins-dockerfile/Dockerfile | 2 +- .../plugins/gogs/GogsConfigHandler.java | 131 +++++++----- .../plugins/gogs/GogsWebHookJenkinsTest.java | 93 ++++++++ .../plugins/gogs/GogsWebHookTest.java | 51 ++--- .../plugins/gogs/GogsWebHook_IT.java | 199 +++++++++++------- .../Gogs-config-json/webHookDefinition.json | 2 +- ...n_1.json => webHookDefinition_branch.json} | 2 +- .../Jenkins-config/test-project-branch.xml | 37 ++++ 14 files changed, 451 insertions(+), 219 deletions(-) create mode 100644 src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookJenkinsTest.java rename src/test/resources/Gogs-config-json/{webHookDefinition_1.json => webHookDefinition_branch.json} (66%) create mode 100644 src/test/resources/Jenkins-config/test-project-branch.xml diff --git a/Jenkinsfile b/Jenkinsfile index a229fa5..f6f0696 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1 +1 @@ -buildPlugin() +buildPlugin(platforms: ['linux']) \ No newline at end of file diff --git a/pom.xml b/pom.xml index 743b819..a6bf6d0 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ 2.30 gogs-webhook - 1.0.15-SNAPSHOT + 1.0.16-SNAPSHOT hpi @@ -113,6 +113,13 @@ jenkins-client 0.3.7 test + + + + dom4j + dom4j + + org.jenkins-ci.plugins @@ -130,6 +137,11 @@ + + org.codehaus.mojo + findbugs-maven-plugin + 3.0.5 + org.apache.maven.plugins maven-surefire-plugin diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsProjectProperty.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsProjectProperty.java index b32f4de..117c8ed 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsProjectProperty.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsProjectProperty.java @@ -23,33 +23,38 @@ associated documentation files (the "Software"), to deal in the Software without package org.jenkinsci.plugins.gogs; +import java.util.logging.Logger; + import hudson.Extension; import hudson.model.Job; import hudson.model.JobProperty; import hudson.model.JobPropertyDescriptor; +import hudson.util.Secret; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; -import javax.annotation.Nonnull; -import java.util.logging.Logger; - @SuppressWarnings("ALL") public class GogsProjectProperty extends JobProperty> { private static final Logger LOGGER = Logger.getLogger(GogsWebHook.class.getName()); - private final String gogsSecret; + private final Secret gogsSecret; private final boolean gogsUsePayload; private final String gogsBranchFilter; - @DataBoundConstructor + @Deprecated public GogsProjectProperty(String gogsSecret, boolean gogsUsePayload, String gogsBranchFilter) { + this(Secret.fromString(gogsSecret), gogsUsePayload, gogsBranchFilter); + } + + @DataBoundConstructor + public GogsProjectProperty(Secret gogsSecret, boolean gogsUsePayload, String gogsBranchFilter) { this.gogsSecret = gogsSecret; this.gogsUsePayload = gogsUsePayload; this.gogsBranchFilter = gogsBranchFilter; } - public String getGogsSecret() { + public Secret getGogsSecret() { return this.gogsSecret; } @@ -75,12 +80,12 @@ public boolean filterBranch(String ref) { @Extension public static final class DescriptorImpl extends JobPropertyDescriptor { public static final String GOGS_PROJECT_BLOCK_NAME = "gogsProject"; - private String gogsSecret; + private Secret gogsSecret; private boolean gogsUsePayload; private String gogsBranchFilter; public String getGogsSecret() { - return gogsSecret; + return Secret.toString(gogsSecret); } public boolean getGogsUsePayload() { @@ -91,14 +96,18 @@ public String getGogsBranchFilter() { return gogsBranchFilter; } - public JobProperty newInstance(@Nonnull StaplerRequest req, @Nonnull JSONObject formData) { - GogsProjectProperty tpp = req.bindJSON( - GogsProjectProperty.class, - formData.getJSONObject(GOGS_PROJECT_BLOCK_NAME) - ); + public JobProperty newInstance(StaplerRequest req, JSONObject formData) { + GogsProjectProperty tpp = null; + + if (req != null) { + tpp = req.bindJSON( + GogsProjectProperty.class, + formData.getJSONObject(GOGS_PROJECT_BLOCK_NAME) + ); + } + if (tpp != null) { LOGGER.finest(formData.toString()); - LOGGER.finest(tpp.gogsSecret); LOGGER.finest(tpp.gogsBranchFilter); gogsSecret = tpp.gogsSecret; diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java index 49e9866..0fe2bb2 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsUtils.java @@ -1,16 +1,19 @@ package org.jenkinsci.plugins.gogs; -import hudson.model.Item; -import jenkins.model.Jenkins; -import org.apache.commons.codec.binary.Hex; - import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.regex.Pattern; import java.util.stream.Collectors; +import hudson.model.Item; +import jenkins.model.Jenkins; +import org.apache.commons.codec.binary.Hex; + class GogsUtils { private GogsUtils() { @@ -23,7 +26,7 @@ private GogsUtils() { * @return the Job matching the given name, or {@code null} when not found */ static T find(String jobName, Class type) { - Jenkins jenkins = Jenkins.getInstance(); + Jenkins jenkins = Jenkins.get(); // direct search, can be used to find folder based items // T item = jenkins.getItemByFullName(jobName, type); if (item == null) { @@ -43,7 +46,20 @@ static T find(String jobName, Class type) { static Map splitQuery(String qs) { return Pattern.compile("&").splitAsStream(qs) .map(p -> p.split("=")) - .collect(Collectors.toMap(a -> a[0], a -> a.length > 1 ? a[1] : "")); + .collect(Collectors.toMap(a -> a[0], a -> a.length > 1 ? urlDecode(a[1]) : "")); + } + + /** + * Decode URL + * + * @param s String to decode + * @return returns decoded string + */ + static String urlDecode(String s) { + try { + return URLDecoder.decode(s, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException ignored) { } + return ""; } /** diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java index 98ff3bd..883d488 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java @@ -23,15 +23,8 @@ associated documentation files (the "Software"), to deal in the Software without package org.jenkinsci.plugins.gogs; -import hudson.Extension; -import hudson.model.Job; -import hudson.model.UnprotectedRootAction; -import hudson.security.ACL; -import hudson.security.ACLContext; -import net.sf.json.JSONObject; -import org.apache.commons.io.IOUtils; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Strings.isNullOrEmpty; import java.io.IOException; import java.io.PrintWriter; @@ -45,8 +38,16 @@ associated documentation files (the "Software"), to deal in the Software without import java.util.regex.Pattern; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Strings.isNullOrEmpty; +import hudson.Extension; +import hudson.model.Job; +import hudson.model.UnprotectedRootAction; +import hudson.security.ACL; +import hudson.security.ACLContext; +import hudson.util.Secret; +import net.sf.json.JSONObject; +import org.apache.commons.io.IOUtils; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; /** * @author Alexander Verhaar @@ -59,6 +60,7 @@ public class GogsWebHook implements UnprotectedRootAction { private String gogsDelivery = null; private String gogsSignature = null; private String jobName = null; + private String xGogsEvent = null; static final String URLNAME = "gogs-webhook"; public String getDisplayName() { @@ -104,6 +106,10 @@ private void setGogsSignature(String gogsSignature) { */ @SuppressWarnings("WeakerAccess") public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException { + AtomicReference jSecret = new AtomicReference<>(null); + AtomicBoolean foundJob = new AtomicBoolean(false); + AtomicBoolean isRefMatched = new AtomicBoolean(true); + GogsPayloadProcessor payloadProcessor = new GogsPayloadProcessor(); GogsCause gogsCause = new GogsCause(); @@ -119,10 +125,11 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException // Get the POST stream String body = IOUtils.toString(req.getInputStream(), DEFAULT_CHARSET); - if (!body.isEmpty() && req.getRequestURI().contains("/" + URLNAME + "/")) { - JSONObject jsonObject = JSONObject.fromObject(body); + JSONObject jsonObject = JSONObject.fromObject(body); + String ref = jsonObject.optString("ref", null); + + if (xGogsEvent.equals("push") && req.getRequestURI().contains("/" + URLNAME + "/") && !body.isEmpty()) { JSONObject commits = (JSONObject) jsonObject.getJSONArray("commits").get(0); - String ref = jsonObject.getString("ref"); String message = commits.getString("message"); if (message.startsWith("[IGNORE]")) { @@ -140,9 +147,6 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException body = body.substring(8); } - AtomicReference jSecret = new AtomicReference<>(null); - AtomicBoolean foundJob = new AtomicBoolean(false); - AtomicBoolean isRefMatched = new AtomicBoolean(true); gogsCause.setGogsPayloadData(jsonObject.toString()); gogsCause.setDeliveryID(getGogsDelivery()); payloadProcessor.setCause(gogsCause); @@ -157,12 +161,30 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException foundJob.set(true); final GogsProjectProperty property = (GogsProjectProperty) job.getProperty(GogsProjectProperty.class); if (property != null) { /* only if Gogs secret is defined on the job */ - jSecret.set(property.getGogsSecret()); /* Secret provided by Jenkins */ + jSecret.set(Secret.toString(property.getGogsSecret())); /* Secret provided by Jenkins */ isRefMatched.set(property.filterBranch(ref)); } }); } + } else { + result.setStatus(404, "No payload or URI contains invalid entries."); + } + // Gogs release event + if (xGogsEvent.equals("release")) { + try (ACLContext ignored = ACL.as(ACL.SYSTEM)) { + /* secret is stored in the properties of Job */ + Stream.of(jobName).map(j -> GogsUtils.find(j, Job.class)).filter(Objects::nonNull).forEach(job -> { + foundJob.set(true); + final GogsProjectProperty property = (GogsProjectProperty) job.getProperty(GogsProjectProperty.class); + if (property != null) { /* only if Gogs secret is defined on the job */ + jSecret.set(Secret.toString(property.getGogsSecret())); /* Secret provided by Jenkins */ + } + }); + } + } + + if (result.getStatus() == 200) { String gSecret = null; if (getGogsSignature() == null) { gSecret = jsonObject.optString("secret", null); /* Secret provided by Gogs < 0.10.x */ @@ -195,8 +217,6 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException /* Gogs and Jenkins secrets differs */ result.setStatus(403, "Incorrect secret"); } - } else { - result.setStatus(404, "No payload or URI contains invalid entries."); } exitWebHook(result, rsp); @@ -215,8 +235,9 @@ private boolean sanityChecks(StaplerRequest req, StaplerResponse rsp) throws IOE checkNotNull(rsp, "Null reply submitted to doIndex method"); // Get X-Gogs-Event - if (!"push".equals(req.getHeader("X-Gogs-Event"))) { - result.setStatus(403, "Only push event can be accepted."); + xGogsEvent = req.getHeader("X-Gogs-Event"); + if (!"push".equals(xGogsEvent) && !"release".equals(xGogsEvent)) { + result.setStatus(403, "Only push or release events are accepted."); exitWebHook(result, rsp); return false; } diff --git a/src/test/docker/gitserver-dockerfile/setup-gogs.sh b/src/test/docker/gitserver-dockerfile/setup-gogs.sh index 516e6d3..4108720 100644 --- a/src/test/docker/gitserver-dockerfile/setup-gogs.sh +++ b/src/test/docker/gitserver-dockerfile/setup-gogs.sh @@ -38,8 +38,11 @@ curl -v -X POST -s \ -F "retype=${FIRST_USER}" \ ${GITSERVER_URL}/user/sign_up -cat /app/repos-to-mirror +# Create Access token for first user +TOKEN=$(curl -X POST -s -F "name=${FIRST_USER}" -u "${FIRST_USER}:${FIRST_USER}" \ + ${GITSERVER_URL}/api/v1/users/${FIRST_USER}/tokens | sed 's/.*"sha1":"\(.*\)"}/\1/') +echo $TOKEN while IFS= read -r LINE || [[ -n "${LINE}" ]]; do echo "==LINE: ${LINE}" @@ -50,10 +53,10 @@ do # Create repo to migrate curl -v -X POST -s \ + -H "Authorization: token ${TOKEN}" \ -F "uid=1" \ -F "clone_addr=${REMOTE_REPO_URL}" \ -F "repo_name=${REPO_NAME}" \ - -u "${FIRST_USER}:${FIRST_USER}" \ ${GITSERVER_API_URL}/repos/migrate done < /app/repos-to-mirror diff --git a/src/test/docker/jenkins-dockerfile/Dockerfile b/src/test/docker/jenkins-dockerfile/Dockerfile index 8127f39..7065912 100644 --- a/src/test/docker/jenkins-dockerfile/Dockerfile +++ b/src/test/docker/jenkins-dockerfile/Dockerfile @@ -1,4 +1,4 @@ -FROM jenkinsci/jenkins:latest +FROM jenkins/jenkins:lts-alpine # Load the required dependencies for the plugin under test RUN /usr/local/bin/install-plugins.sh git workflow-aggregator pipeline-model-extensions cloudbees-folder diff --git a/src/test/java/org/jenkinsci/plugins/gogs/GogsConfigHandler.java b/src/test/java/org/jenkinsci/plugins/gogs/GogsConfigHandler.java index 260b368..b876296 100644 --- a/src/test/java/org/jenkinsci/plugins/gogs/GogsConfigHandler.java +++ b/src/test/java/org/jenkinsci/plugins/gogs/GogsConfigHandler.java @@ -1,13 +1,5 @@ package org.jenkinsci.plugins.gogs; -import net.sf.json.JSONObject; -import net.sf.json.JSONSerializer; -import org.apache.http.HttpHost; -import org.apache.http.client.fluent.Executor; -import org.apache.http.client.fluent.Form; -import org.apache.http.client.fluent.Request; -import org.apache.http.entity.ContentType; - import java.io.File; import java.io.IOException; import java.net.MalformedURLException; @@ -17,6 +9,15 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import net.sf.json.JSONArray; +import net.sf.json.JSONObject; +import net.sf.json.JSONSerializer; +import org.apache.http.HttpHost; +import org.apache.http.client.fluent.Executor; +import org.apache.http.client.fluent.Form; +import org.apache.http.client.fluent.Request; +import org.apache.http.entity.ContentType; + /** * A class to handle the configuration of the Gogs server used during the integration tests. */ @@ -28,7 +29,7 @@ public class GogsConfigHandler { private String gogsServer_password; private Executor executor = null; private String gogsServer_apiUrl = null; - + private String gogsAccessToken; /** * Instantiates an object used to handle operations on a Gogs server. @@ -46,6 +47,7 @@ public GogsConfigHandler(String gogsUrl, String user, String password) throws Ma this.gogsServer_port = aURL.getPort(); this.gogsServer_user = user; this.gogsServer_password = password; + this.gogsAccessToken = getGogsAccessToken(); } /** @@ -60,10 +62,10 @@ void waitForServer(int retries, int retryDelay) throws TimeoutException, Interru String testUrl = this.getGogsUrl() + "/"; for (int i = 0; i < retries; i++) { - int status = 0; + int status; try { status = Request.Get(testUrl) - .execute().returnResponse().getStatusLine().getStatusCode(); + .execute().returnResponse().getStatusLine().getStatusCode(); } catch (IOException e) { TimeUnit.SECONDS.sleep(retryDelay); continue; @@ -100,12 +102,16 @@ int createWebHook(String jsonCommand, String projectName) throws IOException { + "/" + projectName + "/hooks"; Executor executor = getExecutor(); + Request request = Request.Post(gogsHooksConfigUrl); + + if (gogsAccessToken != null) { + request.addHeader("Authorization", "token " + gogsAccessToken); + } String result = executor - .execute(Request.Post(gogsHooksConfigUrl).bodyString(jsonCommand, ContentType.APPLICATION_JSON)) + .execute(request.bodyString(jsonCommand, ContentType.APPLICATION_JSON)) .returnContent().asString(); - JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON(result); - + JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON( result ); return jsonObject.getInt("id"); } @@ -137,9 +143,14 @@ void removeHook(String projectName, int hookId) throws IOException { + "/" + projectName + "/hooks/" + hookId; Executor executor = getExecutor(); + Request request = Request.Delete(gogsHooksConfigUrl); + + if (gogsAccessToken != null) { + request.addHeader("Authorization", "token " + gogsAccessToken); + } int result = executor - .execute(Request.Delete(gogsHooksConfigUrl)) + .execute(request) .returnResponse().getStatusLine().getStatusCode(); if (result != 204) { @@ -158,16 +169,20 @@ void createEmptyRepo(String projectName) throws IOException { Executor executor = getExecutor(); String gogsHooksConfigUrl = getGogsServer_apiUrl() + "user/repos"; + Request request = Request.Post(gogsHooksConfigUrl); + + if (this.gogsAccessToken != null) { + request.addHeader("Authorization", "token " + this.gogsAccessToken); + } int result = executor - .execute(Request - .Post(gogsHooksConfigUrl) + .execute(request .bodyForm(Form.form() - .add("name", projectName) - .add("description", "API generated repository") - .add("private", "true") - .add("auto_init", "false") - .build() + .add("name", projectName) + .add("description", "API generated repository") + .add("private", "true") + .add("auto_init", "false") + .build() ) ) .returnResponse().getStatusLine().getStatusCode(); @@ -178,6 +193,20 @@ void createEmptyRepo(String projectName) throws IOException { } } + void removeRepo(String projectName) throws IOException { + Executor executor = getExecutor(); + String gogsHookConfigUrl = getGogsServer_apiUrl() + "repos/" + this.gogsServer_user + "/" + projectName; + Request request = Request.Delete(gogsHookConfigUrl); + + if (this.gogsAccessToken != null) { + request.addHeader("Authorization", "token " + this.gogsAccessToken); + } + + int result = executor.execute(request).returnResponse().getStatusLine().getStatusCode(); + if (result != 204) { + throw new IOException("Repository deletion call did not return the expected value (returned " + result + ")"); + } + } /** * Gets a Executor object. The Executor object allows to cache the authentication data. @@ -189,49 +218,37 @@ private Executor getExecutor() { if (this.executor == null) { HttpHost httpHost = new HttpHost(this.gogsServer_nodeName, this.gogsServer_port); this.executor = Executor.newInstance() - .auth(httpHost, this.gogsServer_user, this.gogsServer_password) - .authPreemptive(httpHost); + .auth(httpHost, this.gogsServer_user, this.gogsServer_password) + .authPreemptive(httpHost); } return this.executor; } + /** + * Get Access token of the user. + * + * @return an access token of the user + */ + public String getGogsAccessToken() { + String resp; + String sha1 = null; + Executor executor = getExecutor(); + try { + resp = executor.execute( + Request.Get(this.getGogsUrl() + "/api/v1/users/" + this.gogsServer_user + "/tokens") + ).returnContent().toString(); + JSONArray jsonArray = JSONArray.fromObject(resp); + if (!jsonArray.isEmpty()) { + sha1 = ((JSONObject) jsonArray.get(0)).getString("sha1"); + } + } catch (IOException e) { } + return sha1; + } + public String getGogsServer_apiUrl() { if (this.gogsServer_apiUrl == null) { this.gogsServer_apiUrl = this.getGogsUrl() + "/api/v1/"; } return gogsServer_apiUrl; } - - public String getGogsServer_nodeName() { - return gogsServer_nodeName; - } - - public void setGogsServer_nodeName(String gogsServer_nodeName) { - this.gogsServer_nodeName = gogsServer_nodeName; - } - - public int getGogsServer_port() { - return gogsServer_port; - } - - public void setGogsServer_port(int gogsServer_port) { - this.gogsServer_port = gogsServer_port; - } - - public String getGogsServer_user() { - return gogsServer_user; - } - - public void setGogsServer_user(String gogsServer_user) { - this.gogsServer_user = gogsServer_user; - } - - public String getGogsServer_password() { - return gogsServer_password; - } - - public void setGogsServer_password(String gogsServer_password) { - this.gogsServer_password = gogsServer_password; - } - } diff --git a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookJenkinsTest.java b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookJenkinsTest.java new file mode 100644 index 0000000..3cf3010 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookJenkinsTest.java @@ -0,0 +1,93 @@ +package org.jenkinsci.plugins.gogs; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.nio.charset.StandardCharsets; + +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import hudson.model.FreeStyleProject; +import hudson.util.Secret; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GogsWebHookJenkinsTest { + final Logger log = LoggerFactory.getLogger(GogsWebHookJenkinsTest.class); + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Test + public void whenJobBranchNotMatchMustReturnError() { + Secret secret = Secret.fromString(null); + String[][] test_vals = { + {null, "master", "true"}, + {null, "dev", "true"}, + {"", "master", "true"}, + {"", "dev", "true"}, + {"*", "master", "true"}, + {"*", "dev", "true"}, + {"dev", "master", "false"}, + {"dev", "dev", "true"}, + {"master", "master", "true"}, + {"master", "dev", "false"}, + }; + + for (int i = 0; i < test_vals.length; ++i) { + String filter = test_vals[i][0]; + String ref = test_vals[i][1]; + boolean ret = Boolean.parseBoolean(test_vals[i][2]); + + GogsProjectProperty property = new GogsProjectProperty(secret, false, filter); + assertSame(String.format("branch filter check failed for [%s -> %s]", ref, filter), ret, property.filterBranch(ref)); + } + + log.info("Test succeeded."); + } + + @Test + @Issue("SECURITY-1438") + public void ensureTheSecretIsEncryptedOnDisk() throws Exception { + Secret secret = Secret.fromString("s3cr3t"); + FreeStyleProject p = prepareProjectWithGogsProperty(secret); + + File configFile = p.getConfigFile().getFile(); + String configFileContent = FileUtils.readFileToString(configFile, StandardCharsets.UTF_8); + assertThat(configFileContent, not(containsString(secret.getPlainText()))); + assertThat(configFileContent, containsString(secret.getEncryptedValue())); + } + + @Test + @Issue("SECURITY-1438") + public void ensureTheSecretIsEncryptedInHtml() throws Exception { + Secret secret = Secret.fromString("s3cr3t"); + FreeStyleProject p = prepareProjectWithGogsProperty(secret); + + JenkinsRule.WebClient wc = j.createWebClient(); + // there are some errors in the page and thus the status is 500 but the content is there + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); + HtmlPage htmlPage = wc.goTo(p.getUrl() + "configure"); + String pageContent = htmlPage.getWebResponse().getContentAsString(); + assertThat(pageContent, not(containsString(secret.getPlainText()))); + assertThat(pageContent, containsString(secret.getEncryptedValue())); + } + + private FreeStyleProject prepareProjectWithGogsProperty(Secret secret) throws Exception { + FreeStyleProject p = j.createFreeStyleProject(); + + GogsProjectProperty prop = new GogsProjectProperty(secret, true, "master"); + p.addProperty(prop); + + p.save(); + + return p; + } +} diff --git a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java index d8334cc..436919c 100644 --- a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java +++ b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java @@ -1,5 +1,17 @@ package org.jenkinsci.plugins.gogs; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; + import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.junit.Before; @@ -17,17 +29,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintWriter; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - public class GogsWebHookTest { final Logger log = LoggerFactory.getLogger(GogsWebHookTest.class); @@ -88,7 +89,7 @@ public void whenEmptyHeaderTypeMustReturnError() throws Exception { //validate that everything was done as planed verify(staplerResponse).setStatus(403); - String expectedOutput = "Only push event can be accepted."; + String expectedOutput = "Only push or release events are accepted."; isExpectedOutput(uniqueFile, expectedOutput); log.info("Test succeeded."); @@ -109,7 +110,7 @@ public void whenWrongHeaderTypeMustReturnError() throws Exception { //validate that everything was done as planed verify(staplerResponse).setStatus(403); - String expectedOutput = "Only push event can be accepted."; + String expectedOutput = "Only push or release events are accepted."; isExpectedOutput(uniqueFile, expectedOutput); log.info("Test succeeded."); @@ -231,30 +232,6 @@ public void whenUriDoesNotContainUrlNameMustReturnError() throws Exception { log.info("Test succeeded."); } - @Test - public void whenJobBranchNotMatchMustReturnError() throws Exception { - Object[][] test_vals = { - {null, "master", true}, - {null, "dev", true}, - {"", "master", true}, - {"", "dev", true}, - {"*", "master", true}, - {"*", "dev", true}, - {"dev", "master", false}, - {"dev", "dev", true}, - {"master", "master", true}, - {"master", "dev", false}, - }; - for (Object[] test_val : test_vals) { - String filter = (String) test_val[0]; - String ref = (String) test_val[1]; - boolean ret = (Boolean) test_val[2]; - GogsProjectProperty property = new GogsProjectProperty(null, false, filter); - assertSame(String.format("branch filter check failed for [%s -> %s]", ref, filter), ret, property.filterBranch(ref)); - } - log.info("Test succeeded."); - } - // // Helper methods // diff --git a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHook_IT.java b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHook_IT.java index 4ff9a0a..bbeb06d 100644 --- a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHook_IT.java +++ b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHook_IT.java @@ -1,5 +1,29 @@ package org.jenkinsci.plugins.gogs; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_BRANCH_SECTION; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_REMOTE_SECTION; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_USER_SECTION; +import static org.jenkinsci.plugins.gogs.JenkinsHandler.waitUntilJenkinsHasBeenStartedUp; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.TimeoutException; + import com.offbytwo.jenkins.JenkinsServer; import com.offbytwo.jenkins.model.Artifact; import com.offbytwo.jenkins.model.BuildWithDetails; @@ -11,7 +35,6 @@ import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.junit.Rule; @@ -22,35 +45,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; -import java.util.Properties; -import java.util.concurrent.TimeoutException; - -import static org.eclipse.jgit.lib.ConfigConstants.*; -import static org.jenkinsci.plugins.gogs.JenkinsHandler.waitUntilJenkinsHasBeenStartedUp; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - //FIXME: the test should run in sequence public class GogsWebHook_IT { - public static final String JENKINS_URL = "http://localhost:8080/"; - public static final String JENKINS_USER = "butler"; - public static final String JENKINS_PASSWORD = "butler"; - public static final String GOGS_URL = "http://localhost:3000"; - public static final String GOGS_USER = "butler"; - public static final String GOGS_PASSWORD = "butler"; - public static final String WEBHOOK_URL = "http://localhost:8080/job/testRep1/build?delay=0"; - public static final String JSON_COMMANDFILE_PATH = "target/test-classes/Gogs-config-json/"; - public static final String JENKINS_CONFIGS_PATH = "target/test-classes/Jenkins-config/"; - public static final String JENKINS_JOB_NAME = "test project"; + private static final String JENKINS_URL = "http://localhost:8080/"; + private static final String JENKINS_USER = "butler"; + private static final String JENKINS_PASSWORD = "butler"; + private static final String GOGS_URL = "http://localhost:3000"; + private static final String GOGS_USER = "butler"; + private static final String GOGS_PASSWORD = "butler"; + private static final String JSON_COMMANDFILE_PATH = "target/test-classes/Gogs-config-json/"; + private static final String JENKINS_CONFIGS_PATH = "target/test-classes/Jenkins-config/"; + private static final String JENKINS_JOB_NAME = "test project"; final Logger log = LoggerFactory.getLogger(GogsWebHook_IT.class); @Rule @@ -63,13 +69,49 @@ protected void starting(Description description) { @Test public void smokeTest_build_masterBranch() throws Exception { + final String projectName = "testRep1"; + doItTest(JENKINS_JOB_NAME, + projectName, + "test-project.xml", + "webHookDefinition.json", + null); + } + + @Test + public void smokeTest_build_testBranch() throws Exception { + final String projectName = "testRep2"; + doItTest(JENKINS_JOB_NAME + "2", + projectName, + "test-project-branch.xml", + "webHookDefinition_branch.json", + "test"); + } + + /** + * Do integration tests + * + * @param jenkinsJobName Jenkins jobname + * @param projectName Gogs project name + * @param jenkinsXML XML file name with Jenkins job definition + * @param webhookDefinition JSON file with webhook definition + * @param branch Branch name to do test on (null means master) + * @throws Exception Something unexpected went wrong + */ + private void doItTest(final String jenkinsJobName, + final String projectName, + final String jenkinsXML, + final String webhookDefinition, + final String branch) throws Exception { + // Default refspec is master + StringBuilder refSpec = new StringBuilder("refs/heads/master:refs/heads/"); + //Instantiate the Gogs Handler object and wait for the server to be available GogsConfigHandler gogsServer = new GogsConfigHandler(GOGS_URL, GOGS_USER, GOGS_PASSWORD); gogsServer.waitForServer(12, 5); //Create the test repository on the server try { - gogsServer.createEmptyRepo("testRep1"); + gogsServer.createEmptyRepo(projectName); } catch (IOException e) { //check for the exist message; if (e.getMessage().contains("422")) { @@ -87,50 +129,52 @@ public void smokeTest_build_masterBranch() throws Exception { StoredConfig config = git.getRepository().getConfig(); config.setString(CONFIG_USER_SECTION, null, "name", "Automated Test"); config.setString(CONFIG_USER_SECTION, null, "email", "test@test.org"); - config.setString(CONFIG_REMOTE_SECTION, "origin", "url", "http://localhost:3000/butler/testRep1.git"); + config.setString(CONFIG_REMOTE_SECTION, "origin", "url", "http://localhost:3000/butler/"+projectName+".git"); config.setString(CONFIG_REMOTE_SECTION, "origin", "fetch", "+refs/heads/*:refs/remotes/origin/*"); config.setString(CONFIG_BRANCH_SECTION, "master", "remote", "origin"); config.setString(CONFIG_BRANCH_SECTION, "master", "merge", "refs/heads/master"); config.save(); - //add the files located there and commit them Status status = git.status().call(); DirCache index = git.add().addFilepattern(".").call(); RevCommit commit = git.commit().setMessage("Repos initialization").call(); log.info("Commit" + commit.getName()); - //push - UsernamePasswordCredentialsProvider user2 = new UsernamePasswordCredentialsProvider("butler", "butler"); + UsernamePasswordCredentialsProvider user2 = new UsernamePasswordCredentialsProvider(GOGS_USER, GOGS_PASSWORD); + + if (branch == null) { + refSpec.append("master"); + } else { + refSpec.append(branch); + } - RefSpec spec = new RefSpec("refs/heads/master:refs/heads/master"); - Iterable resultIterable = git.push() - .setRemote("origin") - .setCredentialsProvider(user2) - .setRefSpecs(spec) - .call(); + RefSpec spec = new RefSpec(refSpec.toString()); + git.push() + .setRemote("origin") + .setCredentialsProvider(user2) + .setRefSpecs(spec) + .setPushAll() + .call(); //Setup the Jenkins job - JenkinsServer jenkins = new JenkinsServer(new URI(JENKINS_URL), JENKINS_USER, JENKINS_PASSWORD); - waitUntilJenkinsHasBeenStartedUp(jenkins); - //Check if the job exist. If not create it. - Job job = jenkins.getJob(JENKINS_JOB_NAME); + Job job = jenkins.getJob(jenkinsJobName); if (job == null) { //Read the job configuration into a string - File jenkinsConfigFile = new File(JENKINS_CONFIGS_PATH + "test-project.xml"); + File jenkinsConfigFile = new File(JENKINS_CONFIGS_PATH + jenkinsXML); byte[] encoded = Files.readAllBytes(jenkinsConfigFile.toPath()); String configXml = new String(encoded, Charset.defaultCharset()); - jenkins.createJob(JENKINS_JOB_NAME, configXml); + jenkins.createJob(jenkinsJobName, configXml); } //Get the expected build number - JobWithDetails jobAtIntitalState = jenkins.getJob(JENKINS_JOB_NAME); + JobWithDetails jobAtIntitalState = jenkins.getJob(jenkinsJobName); int expectedBuildNbr = jobAtIntitalState.getNextBuildNumber(); log.info("Next build number: " + expectedBuildNbr); @@ -139,21 +183,21 @@ public void smokeTest_build_masterBranch() throws Exception { //Wait for the job to complete long timeOut = 60000L; - waitForBuildToComplete(jenkins, expectedBuildNbr, timeOut); + waitForBuildToComplete(jenkins, expectedBuildNbr, timeOut, jenkinsJobName); //Get the data we stored in the marker file and check it - Properties markerAsProperty = loadMarkerArtifactAsProperty(jenkins); + Properties markerAsProperty = loadMarkerArtifactAsProperty(jenkins, jenkinsJobName); String buildedCommit = markerAsProperty.getProperty("GIT_COMMIT"); - assertEquals("Not the expected GIT commit", commit.getName(), buildedCommit); + assertEquals("Not the expected GIT commit", commit.getName(), buildedCommit); //add the trigger to Gogs - File jsonCommandFile = new File(JSON_COMMANDFILE_PATH + "webHookDefinition.json"); - int hookId = gogsServer.createWebHook(jsonCommandFile, "testRep1"); + File jsonCommandFile = new File(JSON_COMMANDFILE_PATH + webhookDefinition); + int hookId = gogsServer.createWebHook(jsonCommandFile, projectName); log.info("Created hook with ID " + hookId); //Get what is the next build number of the test jenkins job - jobAtIntitalState = jenkins.getJob(JENKINS_JOB_NAME); + jobAtIntitalState = jenkins.getJob(jenkinsJobName); expectedBuildNbr = jobAtIntitalState.getNextBuildNumber(); //change the source file @@ -164,34 +208,37 @@ public void smokeTest_build_masterBranch() throws Exception { RevCommit commitForHook = git.commit().setMessage("Small test modification").call(); log.info("Commit" + commitForHook.getName()); git.push() - .setRemote("origin") - .setCredentialsProvider(user2) - .setRefSpecs(spec) - .call(); + .setRemote("origin") + .setCredentialsProvider(user2) + .setRefSpecs(spec) + .setPushAll() + .call(); - //wait for the build - waitForBuildToComplete(jenkins, expectedBuildNbr, timeOut); - - //Get the data we stored in the marker file and check it - Properties hookMarkerAsProperty = loadMarkerArtifactAsProperty(jenkins); - String hookBuildedCommit = hookMarkerAsProperty.getProperty("GIT_COMMIT"); - assertEquals("Not the expected GIT commit", commitForHook.getName(), hookBuildedCommit); - - //Cleanup - remove the hook we created - gogsServer.removeHook("demoApp", hookId); + try { + //wait for the build + waitForBuildToComplete(jenkins, expectedBuildNbr, timeOut, jenkinsJobName); + + //Get the data we stored in the marker file and check it + Properties hookMarkerAsProperty = loadMarkerArtifactAsProperty(jenkins, jenkinsJobName); + String hookBuildedCommit = hookMarkerAsProperty.getProperty("GIT_COMMIT"); + assertEquals("Not the expected GIT commit", commitForHook.getName(), hookBuildedCommit); + } finally { + // Cleanup the mess we made + gogsServer.removeHook(projectName, hookId); + gogsServer.removeRepo(projectName); + } } - /** * Loads the marker file of the last build (archived during the build) * * @param jenkins the jenkins instance we want to load from * @return the marker file loaded as a property file (so that it can be easily queried) * @throws IOException Something unexpected went wrong when querying the Jenkins server - * @throws URISyntaxException Something uunexpected went wrong loading the marker as a property + * @throws URISyntaxException Something unexpected went wrong loading the marker as a property */ - private Properties loadMarkerArtifactAsProperty(JenkinsServer jenkins) throws IOException, URISyntaxException { - JobWithDetails detailedJob = jenkins.getJob(JENKINS_JOB_NAME); + private Properties loadMarkerArtifactAsProperty(JenkinsServer jenkins, String jobName) throws IOException, URISyntaxException { + JobWithDetails detailedJob = jenkins.getJob(jobName); BuildWithDetails lastBuild = detailedJob.getLastBuild().details(); int buildNbr = lastBuild.getNumber(); boolean isBuilding = lastBuild.isBuilding(); @@ -223,13 +270,13 @@ private Properties loadMarkerArtifactAsProperty(JenkinsServer jenkins) throws IO * @param fileName the source file to modify * @throws IOException something went wrong when updating the file */ - private void changeTheSourceFile(String fileName) throws IOException { + private void changeTheSourceFile(@SuppressWarnings("SameParameterValue") String fileName) throws IOException { Writer output; - boolean openInAppendMode = true; - output = new BufferedWriter(new FileWriter(fileName, openInAppendMode)); + output = new BufferedWriter(new FileWriter(fileName, true)); Date dNow = new Date(); SimpleDateFormat ft = new SimpleDateFormat("E yyyy.MM.dd 'at' hh:mm:ss a zzz"); - output.append("\nThe file was modified by the test application : " + ft.format(dNow) + " \n"); + String outString = "\nThe file was modified by the test application : " + ft.format(dNow) + " \n"; + output.append(outString); output.close(); } @@ -243,9 +290,9 @@ private void changeTheSourceFile(String fileName) throws IOException { * @throws TimeoutException we exeeded the timeout period. * @throws IOException an unexpected error occurred while communicating with Jenkins */ - private void waitForBuildToComplete(JenkinsServer jenkins, int expectedBuildNbr, long timeOut) throws InterruptedException, TimeoutException, IOException { + private void waitForBuildToComplete(JenkinsServer jenkins, int expectedBuildNbr, long timeOut, String jobname) throws InterruptedException, TimeoutException, IOException { boolean buildCompleted = false; - Long timeoutCounter = 0L; + long timeoutCounter = 0L; while (!buildCompleted) { Thread.sleep(2000); timeoutCounter = timeoutCounter + 2000L; @@ -254,7 +301,7 @@ private void waitForBuildToComplete(JenkinsServer jenkins, int expectedBuildNbr, } //When the build is in the queue, the nextbuild number didn't change. //When it changed, It might still be running. - JobWithDetails wrkJobData = jenkins.getJob(JENKINS_JOB_NAME); + JobWithDetails wrkJobData = jenkins.getJob(jobname); int newNextNbr = wrkJobData.getNextBuildNumber(); log.info("New Next Nbr:" + newNextNbr); if (expectedBuildNbr != newNextNbr) { diff --git a/src/test/resources/Gogs-config-json/webHookDefinition.json b/src/test/resources/Gogs-config-json/webHookDefinition.json index d60d18c..d765fce 100644 --- a/src/test/resources/Gogs-config-json/webHookDefinition.json +++ b/src/test/resources/Gogs-config-json/webHookDefinition.json @@ -2,7 +2,7 @@ "type":"gogs", "config": { - "url":"http://jenkins:8080/job/test%20project/build?delay=0", + "url":"http://jenkins:8080/gogs-webhook/?job=test%20project", "content_type":"json" }, "events":["create","push","pull_request"], diff --git a/src/test/resources/Gogs-config-json/webHookDefinition_1.json b/src/test/resources/Gogs-config-json/webHookDefinition_branch.json similarity index 66% rename from src/test/resources/Gogs-config-json/webHookDefinition_1.json rename to src/test/resources/Gogs-config-json/webHookDefinition_branch.json index 010e32d..9a3e0e6 100644 --- a/src/test/resources/Gogs-config-json/webHookDefinition_1.json +++ b/src/test/resources/Gogs-config-json/webHookDefinition_branch.json @@ -2,7 +2,7 @@ "type":"gogs", "config": { - "url":"http://localhost:8080/job/demoapp/build?delay=0", + "url":"http://jenkins:8080/gogs-webhook/?job=test%20project2", "content_type":"json" }, "events":["create","push","pull_request"], diff --git a/src/test/resources/Jenkins-config/test-project-branch.xml b/src/test/resources/Jenkins-config/test-project-branch.xml new file mode 100644 index 0000000..2952f9e --- /dev/null +++ b/src/test/resources/Jenkins-config/test-project-branch.xml @@ -0,0 +1,37 @@ + + + + + false + + + + + + test + + + + + 2 + + + http://gitserver:3000/butler/testRep2 + GOGS-USER + + + + + */test + + + false + + + + Jenkinsfile + false + + + false + \ No newline at end of file From c4ec8e0c931f1285c2a3bb2d859f050a8a77258e Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Tue, 23 Jul 2019 23:01:30 +0200 Subject: [PATCH 17/19] Fix for sending invalid JSON --- pom.xml | 4 ++-- .../java/org/jenkinsci/plugins/gogs/GogsWebHook.java | 11 ++++++++++- .../org/jenkinsci/plugins/gogs/GogsWebHookTest.java | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index a6bf6d0..44a4bd1 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ false - 2.9.7 + 2.9.9 Jenkins Gogs plugin @@ -57,7 +57,7 @@ org.jenkins-ci.plugins git - 3.8.0 + 3.10.0 org.apache.httpcomponents diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java index 883d488..6c5c979 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java @@ -44,6 +44,7 @@ associated documentation files (the "Software"), to deal in the Software without import hudson.security.ACL; import hudson.security.ACLContext; import hudson.util.Secret; +import net.sf.json.JSONException; import net.sf.json.JSONObject; import org.apache.commons.io.IOUtils; import org.kohsuke.stapler.StaplerRequest; @@ -106,6 +107,8 @@ private void setGogsSignature(String gogsSignature) { */ @SuppressWarnings("WeakerAccess") public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException { + JSONObject jsonObject = null; + AtomicReference jSecret = new AtomicReference<>(null); AtomicBoolean foundJob = new AtomicBoolean(false); AtomicBoolean isRefMatched = new AtomicBoolean(true); @@ -125,7 +128,13 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException // Get the POST stream String body = IOUtils.toString(req.getInputStream(), DEFAULT_CHARSET); - JSONObject jsonObject = JSONObject.fromObject(body); + try { + jsonObject = JSONObject.fromObject(body); + } catch (JSONException e) { + result.setStatus(400, "Invalid JSON"); + exitWebHook(result, rsp); + return; + } String ref = jsonObject.optString("ref", null); if (xGogsEvent.equals("push") && req.getRequestURI().contains("/" + URLNAME + "/") && !body.isEmpty()) { diff --git a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java index 436919c..0a42197 100644 --- a/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java +++ b/src/test/java/org/jenkinsci/plugins/gogs/GogsWebHookTest.java @@ -224,9 +224,9 @@ public void whenUriDoesNotContainUrlNameMustReturnError() throws Exception { performDoIndexTest(staplerRequest, staplerResponse, uniqueFile); //validate that everything was done as planed - verify(staplerResponse).setStatus(404); + verify(staplerResponse).setStatus(400); - String expectedOutput = "No payload or URI contains invalid entries."; + String expectedOutput = "Invalid JSON"; isExpectedOutput(uniqueFile, expectedOutput); log.info("Test succeeded."); From 9136b53d699aec5cd60dce4bfe47eb76ac565191 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Wed, 24 Jul 2019 19:13:04 +0200 Subject: [PATCH 18/19] Fix duplicated build key in pom.xml --- pom.xml | 185 +++++++++++++++++++++++++++----------------------------- 1 file changed, 89 insertions(+), 96 deletions(-) diff --git a/pom.xml b/pom.xml index 0f38e7e..77d8b0d 100644 --- a/pom.xml +++ b/pom.xml @@ -135,92 +135,97 @@ - - - - org.codehaus.mojo - findbugs-maven-plugin - 3.0.5 - - - org.apache.maven.plugins - maven-surefire-plugin - 2.21.0 - - ${jaCoCoArgLine} - - **/IT*.java - - - true - - - - - default-test - test - - test - - - - - - org.jacoco - jacoco-maven-plugin - 0.8.1 - - - default-prepare-agent - - prepare-agent - - - jaCoCoArgLine - - - - default-report - - report - - - - default-check - - check - + + + default + + true + + + + + org.codehaus.mojo + findbugs-maven-plugin + 3.0.5 + + + org.apache.maven.plugins + maven-surefire-plugin + 2.21.0 - - - CLASS - - - LINE - COVEREDRATIO - 0.0 - - - BRANCH - COVEREDRATIO - 0.0 - - - - + ${jaCoCoArgLine} + + **/IT*.java + + + true + - - - - - org.eluder.coveralls - coveralls-maven-plugin - 4.3.0 - - - - - + + + default-test + test + + test + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.1 + + + default-prepare-agent + + prepare-agent + + + jaCoCoArgLine + + + + default-report + + report + + + + default-check + + check + + + + + CLASS + + + LINE + COVEREDRATIO + 0.0 + + + BRANCH + COVEREDRATIO + 0.0 + + + + + + + + + + org.eluder.coveralls + coveralls-maven-plugin + 4.3.0 + + + + withIntegrationTest From c860205f67c8bef289406c2d0075f693dc8ee3b6 Mon Sep 17 00:00:00 2001 From: Alexander Verhaar Date: Wed, 24 Jul 2019 19:27:14 +0200 Subject: [PATCH 19/19] Fix --- .../java/org/jenkinsci/plugins/gogs/GogsWebHook.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java index adb3c62..6c5c979 100644 --- a/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java +++ b/src/main/java/org/jenkinsci/plugins/gogs/GogsWebHook.java @@ -47,7 +47,6 @@ associated documentation files (the "Software"), to deal in the Software without import net.sf.json.JSONException; import net.sf.json.JSONObject; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -149,17 +148,6 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException return; } - String ref = jsonObject.getString("ref"); - LOGGER.fine("found ref " + ref); - LOGGER.fine("found branch " + branchName); - if (null != branchName && !StringUtils.containsIgnoreCase(ref, (String) branchName)) { - // ignore all commit if they is not in context - LOGGER.fine("build was rejected"); - result.setStatus(200, String.format("Commit is not relevant. Relevant context is %s", branchName)); - exitWebHook(result, rsp); - return; - } - String contentType = req.getContentType(); if (contentType != null && contentType.startsWith("application/x-www-form-urlencoded")) { body = URLDecoder.decode(body, DEFAULT_CHARSET);