@@ -30,26 +30,21 @@ associated documentation files (the "Software"), to deal in the Software without
3030import net .sf .json .JSONObject ;
3131import org .acegisecurity .context .SecurityContext ;
3232import org .acegisecurity .context .SecurityContextHolder ;
33- import org .apache .commons .codec .binary .Hex ;
3433import org .apache .commons .io .IOUtils ;
3534import org .kohsuke .stapler .StaplerRequest ;
3635import org .kohsuke .stapler .StaplerResponse ;
3736
38- import javax .annotation .Nonnull ;
39- import javax .crypto .Mac ;
40- import javax .crypto .spec .SecretKeySpec ;
4137import java .io .IOException ;
4238import java .io .PrintWriter ;
4339import java .net .URLDecoder ;
44- import java .nio .charset .Charset ;
45- import java .util .Arrays ;
4640import java .util .Map ;
41+ import java .util .Objects ;
4742import java .util .StringJoiner ;
4843import java .util .concurrent .atomic .AtomicBoolean ;
4944import java .util .concurrent .atomic .AtomicReference ;
5045import java .util .logging .Logger ;
5146import java .util .regex .Pattern ;
52- import java .util .stream .Collectors ;
47+ import java .util .stream .Stream ;
5348
5449import static com .google .common .base .Preconditions .checkNotNull ;
5550import static com .google .common .base .Strings .isNullOrEmpty ;
@@ -60,8 +55,12 @@ associated documentation files (the "Software"), to deal in the Software without
6055@ Extension
6156public class GogsWebHook implements UnprotectedRootAction {
6257 private final static Logger LOGGER = Logger .getLogger (GogsWebHook .class .getName ());
63- static final String URLNAME = "gogs-webhook" ;
6458 private static final String DEFAULT_CHARSET = "UTF-8" ;
59+ private GogsResults result = new GogsResults ();
60+ private String gogsDelivery = null ;
61+ private String gogsSignature = null ;
62+ private String jobName = null ;
63+ static final String URLNAME = "gogs-webhook" ;
6564
6665 public String getDisplayName () {
6766 return null ;
@@ -75,21 +74,26 @@ public String getUrlName() {
7574 return URLNAME ;
7675 }
7776
78- /**
79- * encode sha256 hmac
80- *
81- * @param data data to hex
82- * @param key key of HmacSHA256
83- * @return a String with the encoded sha256 hmac
84- * @throws Exception Something went wrong getting the sha256 hmac
85- */
86- private static @ Nonnull
87- String encode (String data , String key ) throws Exception {
88- final Charset asciiCs = Charset .forName ("UTF-8" );
89- final Mac sha256_HMAC = Mac .getInstance ("HmacSHA256" );
90- final SecretKeySpec secret_key = new javax .crypto .spec .SecretKeySpec (asciiCs .encode (key ).array (), "HmacSHA256" );
91- sha256_HMAC .init (secret_key );
92- return Hex .encodeHexString (sha256_HMAC .doFinal (data .getBytes ("UTF-8" )));
77+ private String getGogsDelivery () {
78+ if (isNullOrEmpty (gogsDelivery )) {
79+ return "Triggered by Jenkins-Gogs-Plugin. Delivery ID unknown." ;
80+ }
81+ return "Gogs-ID: " + gogsDelivery ;
82+ }
83+
84+ private void setGogsDelivery (String gogsDelivery ) {
85+ this .gogsDelivery = gogsDelivery ;
86+ }
87+
88+ private String getGogsSignature () {
89+ if (isNullOrEmpty (gogsSignature )) {
90+ gogsSignature = null ;
91+ }
92+ return gogsSignature ;
93+ }
94+
95+ private void setGogsSignature (String gogsSignature ) {
96+ this .gogsSignature = gogsSignature ;
9397 }
9498
9599 /**
@@ -100,52 +104,17 @@ String encode(String data, String key) throws Exception {
100104 * @throws IOException problem while parsing
101105 */
102106 public void doIndex (StaplerRequest req , StaplerResponse rsp ) throws IOException {
103- GogsResults result = new GogsResults ();
104107 GogsPayloadProcessor payloadProcessor = new GogsPayloadProcessor ();
105108
106- //Check that we have something to process
107- checkNotNull (req , "Null request submitted to doIndex method" );
108- checkNotNull (rsp , "Null reply submitted to doIndex method" );
109-
110- // Get X-Gogs-Event
111- String event = req .getHeader ("X-Gogs-Event" );
112- if (!"push" .equals (event )) {
113- result .setStatus (403 , "Only push event can be accepted." );
114- exitWebHook (result , rsp );
109+ if (!sanityChecks (req , rsp )) {
115110 return ;
116111 }
117112
118113 // Get X-Gogs-Delivery header with deliveryID
119- String gogsDelivery = req .getHeader ("X-Gogs-Delivery" );
120- if (isNullOrEmpty (gogsDelivery )) {
121- gogsDelivery = "Triggered by Jenkins-Gogs-Plugin. Delivery ID unknown." ;
122- } else {
123- gogsDelivery = "Gogs-ID: " + gogsDelivery ;
124- }
114+ setGogsDelivery (req .getHeader ("X-Gogs-Delivery" ));
125115
126116 // Get X-Gogs-Signature
127- String gogsSignature = req .getHeader ("X-Gogs-Signature" );
128- if (isNullOrEmpty (gogsSignature )) {
129- gogsSignature = null ;
130- }
131-
132-
133- // Get queryStringMap from the URI
134- String queryString = checkNotNull (req .getQueryString (), "The queryString in the request is null" );
135- Map queryStringMap = checkNotNull (splitQuery (queryString ), "Null queryStringMap" );
136-
137- //Do we have the job name parameter ?
138- if (!queryStringMap .containsKey ("job" )) {
139- result .setStatus (404 , "Parameter 'job' is missing." );
140- exitWebHook (result , rsp );
141- return ;
142- }
143- String jobName = queryStringMap .get ("job" ).toString ();
144- if (isNullOrEmpty (jobName )) {
145- result .setStatus (404 , "No value assigned to parameter 'job'" );
146- exitWebHook (result , rsp );
147- return ;
148- }
117+ setGogsSignature (req .getHeader ("X-Gogs-Signature" ));
149118
150119 // Get the POST stream
151120 String body = IOUtils .toString (req .getInputStream (), DEFAULT_CHARSET );
@@ -182,27 +151,24 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException
182151 .forEach (stringJoiner ::add );
183152 String ref = stringJoiner .toString ();
184153
185- Arrays .asList (jobName , jobName + "/" + ref ).forEach (j -> {
186- Job job = GogsUtils .find (j , Job .class );
187- if (job != null ) {
188- foundJob .set (true );
189- /* secret is stored in the properties of Job */
190- final GogsProjectProperty property = (GogsProjectProperty ) job .getProperty (GogsProjectProperty .class );
191- if (property != null ) { /* only if Gogs secret is defined on the job */
192- jSecret .set (property .getGogsSecret ()); /* Secret provided by Jenkins */
193- }
154+ /* secret is stored in the properties of Job */
155+ Stream .of (jobName , jobName + "/" + ref ).map (j -> GogsUtils .find (j , Job .class )).filter (Objects ::nonNull ).forEach (job -> {
156+ foundJob .set (true );
157+ final GogsProjectProperty property = (GogsProjectProperty ) job .getProperty (GogsProjectProperty .class );
158+ if (property != null ) { /* only if Gogs secret is defined on the job */
159+ jSecret .set (property .getGogsSecret ()); /* Secret provided by Jenkins */
194160 }
195161 });
196162 } finally {
197163 SecurityContextHolder .setContext (saveCtx );
198164 }
199165
200166 String gSecret = null ;
201- if (gogsSignature == null ) {
167+ if (getGogsSignature () == null ) {
202168 gSecret = jsonObject .optString ("secret" , null ); /* Secret provided by Gogs < 0.10.x */
203169 } else {
204170 try {
205- if (gogsSignature .equals (encode (body , jSecret .get ()))) {
171+ if (getGogsSignature () .equals (GogsUtils . encode (body , jSecret .get ()))) {
206172 gSecret = jSecret .get ();
207173 // now hex is right, continue to old logic
208174 }
@@ -217,10 +183,10 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException
217183 LOGGER .warning (msg );
218184 } else if (isNullOrEmpty (jSecret .get ()) && isNullOrEmpty (gSecret )) {
219185 /* No password is set in Jenkins and Gogs, run without secrets */
220- result = payloadProcessor .triggerJobs (jobName , gogsDelivery );
186+ result = payloadProcessor .triggerJobs (jobName , getGogsDelivery () );
221187 } else if (!isNullOrEmpty (jSecret .get ()) && jSecret .get ().equals (gSecret )) {
222188 /* Password is set in Jenkins and Gogs, and is correct */
223- result = payloadProcessor .triggerJobs (jobName , gogsDelivery );
189+ result = payloadProcessor .triggerJobs (jobName , getGogsDelivery () );
224190 } else {
225191 /* Gogs and Jenkins secrets differs */
226192 result .setStatus (403 , "Incorrect secret" );
@@ -232,6 +198,45 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException
232198 exitWebHook (result , rsp );
233199 }
234200
201+ /***
202+ * Do sanity checks
203+ *
204+ * @param req Request
205+ * @param rsp Response
206+ * @throws IOException
207+ */
208+ private boolean sanityChecks (StaplerRequest req , StaplerResponse rsp ) throws IOException {
209+ //Check that we have something to process
210+ checkNotNull (req , "Null request submitted to doIndex method" );
211+ checkNotNull (rsp , "Null reply submitted to doIndex method" );
212+
213+ // Get X-Gogs-Event
214+ if (!"push" .equals (req .getHeader ("X-Gogs-Event" ))) {
215+ result .setStatus (403 , "Only push event can be accepted." );
216+ exitWebHook (result , rsp );
217+ return false ;
218+ }
219+
220+ // Get queryStringMap from the URI
221+ String queryString = checkNotNull (req .getQueryString (), "The queryString in the request is null" );
222+ Map queryStringMap = checkNotNull (GogsUtils .splitQuery (queryString ), "Null queryStringMap" );
223+
224+ //Do we have the job name parameter ?
225+ if (!queryStringMap .containsKey ("job" )) {
226+ result .setStatus (404 , "Parameter 'job' is missing." );
227+ exitWebHook (result , rsp );
228+ return false ;
229+ }
230+
231+ jobName = queryStringMap .get ("job" ).toString ();
232+ if (isNullOrEmpty (jobName )) {
233+ result .setStatus (404 , "No value assigned to parameter 'job'" );
234+ exitWebHook (result , rsp );
235+ return false ;
236+ }
237+ return true ;
238+ }
239+
235240 /**
236241 * Exit the WebHook
237242 *
@@ -250,18 +255,6 @@ private void exitWebHook(GogsResults result, StaplerResponse resp) throws IOExce
250255 PrintWriter printer = resp .getWriter ();
251256 printer .print (json .toString ());
252257 }
253-
254- /**
255- * Converts Querystring into Map<String,String>
256- *
257- * @param qs Querystring
258- * @return returns map from querystring
259- */
260- private static Map <String , String > splitQuery (String qs ) {
261- return Pattern .compile ("&" ).splitAsStream (qs )
262- .map (p -> p .split ("=" ))
263- .collect (Collectors .toMap (a -> a [0 ], a -> a .length > 1 ? a [1 ] : "" ));
264- }
265258}
266259
267260// vim: set ts=4 sw=4 tw=0 ft=java et :
0 commit comments