From 2aa5fdc2439fe5e2ddfa5adcbaf437882f9b781f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=B6=E7=88=B8?= <1@5.nu> Date: Mon, 19 Mar 2018 14:03:56 +0800 Subject: [PATCH 1/9] add gogs support --- gogs/gogs.go | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++ webhooks.go | 5 +- 2 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 gogs/gogs.go diff --git a/gogs/gogs.go b/gogs/gogs.go new file mode 100644 index 0000000..9475271 --- /dev/null +++ b/gogs/gogs.go @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2018, 奶爸<1@5.nu> + * All rights reserved. + */ + +package gogs + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/naiba/webhooks" + client "github.com/gogits/go-gogs-client" + "crypto/hmac" + "crypto/sha1" + "encoding/hex" +) + +// Webhook instance contains all methods needed to process events +type Webhook struct { + provider webhooks.Provider + secret string + eventFuncs map[Event]webhooks.ProcessPayloadFunc +} + +// Config defines the configuration to create a new Gogs Webhook instance +type Config struct { + Secret string +} + +// Event defines a Gogs hook event type +type Event string + +// Gogs hook types +const ( + CreateEvent Event = "create" + DeleteEvent Event = "delete" + ForkEvent Event = "fork" + PushEvent Event = "push" + IssuesEvent Event = "issues" + IssueCommentEvent Event = "issue_comment" + PullRequestEvent Event = "pull_request" + ReleaseEvent Event = "release" +) + +// New creates and returns a WebHook instance denoted by the Provider type +func New(config *Config) *Webhook { + return &Webhook{ + provider: webhooks.Gogs, + secret: config.Secret, + eventFuncs: map[Event]webhooks.ProcessPayloadFunc{}, + } +} + +// Provider returns the current hooks provider ID +func (hook Webhook) Provider() webhooks.Provider { + return hook.provider +} + +// RegisterEvents registers the function to call when the specified event(s) are encountered +func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) { + + for _, event := range events { + hook.eventFuncs[event] = fn + } +} + +// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists. +func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) { + webhooks.DefaultLog.Info("Parsing Payload...") + + event := r.Header.Get("X-Gogs-Event") + if len(event) == 0 { + webhooks.DefaultLog.Error("Missing X-Gogs-Event Header") + http.Error(w, "400 Bad Request - Missing X-Gogs-Event Header", http.StatusBadRequest) + return + } + webhooks.DefaultLog.Debug(fmt.Sprintf("X-Gogs-Event:%s", event)) + + gogsEvent := Event(event) + + fn, ok := hook.eventFuncs[gogsEvent] + // if no event registered + if !ok { + webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in gogs that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event)) + return + } + + payload, err := ioutil.ReadAll(r.Body) + if err != nil || len(payload) == 0 { + webhooks.DefaultLog.Error("Issue reading Payload") + http.Error(w, "Issue reading Payload", http.StatusInternalServerError) + return + } + webhooks.DefaultLog.Debug(fmt.Sprintf("Payload:%s", string(payload))) + + // If we have a Secret set, we should check the MAC + if len(hook.secret) > 0 { + webhooks.DefaultLog.Info("Checking secret") + signature := r.Header.Get("X-Gogs-Signature") + if len(signature) == 0 { + webhooks.DefaultLog.Error("Missing X-Gogs-Signature required for HMAC verification") + http.Error(w, "403 Forbidden - Missing X-Gogs-Signature required for HMAC verification", http.StatusForbidden) + return + } + webhooks.DefaultLog.Debug(fmt.Sprintf("X-Gogs-Signature:%s", signature)) + + mac := hmac.New(sha1.New, []byte(hook.secret)) + mac.Write(payload) + + expectedMAC := hex.EncodeToString(mac.Sum(nil)) + + if !hmac.Equal([]byte(signature[5:]), []byte(expectedMAC)) { + webhooks.DefaultLog.Error("HMAC verification failed") + http.Error(w, "403 Forbidden - HMAC verification failed", http.StatusForbidden) + return + } + } + + // Make headers available to ProcessPayloadFunc as a webhooks type + hd := webhooks.Header(r.Header) + + switch gogsEvent { + case CreateEvent: + var pe client.CreatePayload + json.Unmarshal([]byte(payload), &pe) + hook.runProcessPayloadFunc(fn, pe, hd) + + case ReleaseEvent: + var re client.ReleasePayload + json.Unmarshal([]byte(payload), &re) + hook.runProcessPayloadFunc(fn, re, hd) + + case PushEvent: + var pe client.PushPayload + json.Unmarshal([]byte(payload), &pe) + hook.runProcessPayloadFunc(fn, pe, hd) + + case DeleteEvent: + var de client.DeletePayload + json.Unmarshal([]byte(payload), &de) + hook.runProcessPayloadFunc(fn, de, hd) + + case ForkEvent: + var fe client.ForkPayload + json.Unmarshal([]byte(payload), &fe) + hook.runProcessPayloadFunc(fn, fe, hd) + + case IssuesEvent: + var ie client.IssuesPayload + json.Unmarshal([]byte(payload), &ie) + hook.runProcessPayloadFunc(fn, ie, hd) + + case IssueCommentEvent: + var ice client.IssueCommentPayload + json.Unmarshal([]byte(payload), &ice) + hook.runProcessPayloadFunc(fn, ice, hd) + + case PullRequestEvent: + var pre client.PullRequestPayload + json.Unmarshal([]byte(payload), &pre) + hook.runProcessPayloadFunc(fn, pre, hd) + } +} + +func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) { + go func(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) { + fn(results, header) + }(fn, results, header) +} diff --git a/webhooks.go b/webhooks.go index 7e2d483..994f748 100644 --- a/webhooks.go +++ b/webhooks.go @@ -19,6 +19,8 @@ func (p Provider) String() string { return "Bitbucket" case GitLab: return "GitLab" + case Gogs: + return "Gogs" default: return "Unknown" } @@ -26,9 +28,10 @@ func (p Provider) String() string { // webhooks available providers const ( - GitHub Provider = iota + GitHub Provider = iota Bitbucket GitLab + Gogs ) // Webhook interface defines a webhook to receive events From 1c3914ef1656967f4170074f638fd8f51935b16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=B6=E7=88=B8?= <1@5.nu> Date: Mon, 19 Mar 2018 14:25:52 +0800 Subject: [PATCH 2/9] edit import --- bitbucket/bitbucket.go | 2 +- github/github.go | 2 +- gitlab/gitlab.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bitbucket/bitbucket.go b/bitbucket/bitbucket.go index 4fa5929..46e9422 100644 --- a/bitbucket/bitbucket.go +++ b/bitbucket/bitbucket.go @@ -6,7 +6,7 @@ import ( "io/ioutil" "net/http" - "gopkg.in/go-playground/webhooks.v3" + "github.com/naiba/webhooks" ) // Webhook instance contains all methods needed to process events diff --git a/github/github.go b/github/github.go index 2b5c933..26a52da 100644 --- a/github/github.go +++ b/github/github.go @@ -9,7 +9,7 @@ import ( "io/ioutil" "net/http" - "gopkg.in/go-playground/webhooks.v3" + "github.com/naiba/webhooks" ) // Webhook instance contains all methods needed to process events diff --git a/gitlab/gitlab.go b/gitlab/gitlab.go index cac3a3e..5126e1d 100644 --- a/gitlab/gitlab.go +++ b/gitlab/gitlab.go @@ -6,7 +6,7 @@ import ( "io/ioutil" "net/http" - "gopkg.in/go-playground/webhooks.v3" + "github.com/naiba/webhooks" ) // Webhook instance contains all methods needed to process events From fc20b2a2509dd312d2735ae38b9450ace98edbf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=B6=E7=88=B8?= <1@5.nu> Date: Mon, 19 Mar 2018 14:26:32 +0800 Subject: [PATCH 3/9] ignore IDEA --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index daf913b..0c9e329 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ # Folders _obj _test +.idea # Architecture specific extensions/prefixes *.[568vq] From 493e94de50ecdd10025b526e3d1305ed7821a487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=B6=E7=88=B8?= <1@5.nu> Date: Mon, 19 Mar 2018 16:16:01 +0800 Subject: [PATCH 4/9] [fix] Gogs signature calc --- gogs/gogs.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/gogs/gogs.go b/gogs/gogs.go index 9475271..da04d59 100644 --- a/gogs/gogs.go +++ b/gogs/gogs.go @@ -14,8 +14,7 @@ import ( "github.com/naiba/webhooks" client "github.com/gogits/go-gogs-client" "crypto/hmac" - "crypto/sha1" - "encoding/hex" + "crypto/sha256" ) // Webhook instance contains all methods needed to process events @@ -107,12 +106,12 @@ func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) { } webhooks.DefaultLog.Debug(fmt.Sprintf("X-Gogs-Signature:%s", signature)) - mac := hmac.New(sha1.New, []byte(hook.secret)) + mac := hmac.New(sha256.New, []byte(hook.secret)) mac.Write(payload) - expectedMAC := hex.EncodeToString(mac.Sum(nil)) + expectedMAC := mac.Sum(nil) - if !hmac.Equal([]byte(signature[5:]), []byte(expectedMAC)) { + if !hmac.Equal([]byte(signature), expectedMAC) { webhooks.DefaultLog.Error("HMAC verification failed") http.Error(w, "403 Forbidden - HMAC verification failed", http.StatusForbidden) return From d4d9692af018d5c7e3433fa4177b1d3c9f6afbb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=B6=E7=88=B8?= <1@5.nu> Date: Tue, 20 Mar 2018 11:27:21 +0800 Subject: [PATCH 5/9] [fix]Gogs sign verify --- gogs/gogs.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gogs/gogs.go b/gogs/gogs.go index da04d59..d478dee 100644 --- a/gogs/gogs.go +++ b/gogs/gogs.go @@ -15,6 +15,7 @@ import ( client "github.com/gogits/go-gogs-client" "crypto/hmac" "crypto/sha256" + "encoding/hex" ) // Webhook instance contains all methods needed to process events @@ -109,10 +110,14 @@ func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) { mac := hmac.New(sha256.New, []byte(hook.secret)) mac.Write(payload) - expectedMAC := mac.Sum(nil) + expectedMAC := hex.EncodeToString(mac.Sum(nil)) - if !hmac.Equal([]byte(signature), expectedMAC) { + if !hmac.Equal([]byte(signature), []byte(expectedMAC)) { webhooks.DefaultLog.Error("HMAC verification failed") + webhooks.DefaultLog.Debug("LocalHMAC:" + expectedMAC) + webhooks.DefaultLog.Debug("RemoteHMAC:" + signature) + webhooks.DefaultLog.Debug("Secret:" + hook.secret) + webhooks.DefaultLog.Debug(string(payload)) http.Error(w, "403 Forbidden - HMAC verification failed", http.StatusForbidden) return } From b26a00f48cb1b4f4bae4657441f28962131c59d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=B6=E7=88=B8?= <1@5.nu> Date: Wed, 11 Apr 2018 16:11:21 +0800 Subject: [PATCH 6/9] remove space --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c2ec894..e4e44a5 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Installation Use go get. ```shell - go get -u gopkg.in/go-playground/webhooks.v3 +go get -u gopkg.in/go-playground/webhooks.v3 ``` Then import the package into your own code. From 22713e3054b584088f55f7e4cfd923aec0223c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=B6=E7=88=B8?= <1@5.nu> Date: Mon, 7 May 2018 18:41:12 +0800 Subject: [PATCH 7/9] add gogs support --- bitbucket/bitbucket.go | 2 +- github/github.go | 2 +- gitlab/gitlab.go | 2 +- gogs/gogs.go | 7 +------ 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/bitbucket/bitbucket.go b/bitbucket/bitbucket.go index 46e9422..4fa5929 100644 --- a/bitbucket/bitbucket.go +++ b/bitbucket/bitbucket.go @@ -6,7 +6,7 @@ import ( "io/ioutil" "net/http" - "github.com/naiba/webhooks" + "gopkg.in/go-playground/webhooks.v3" ) // Webhook instance contains all methods needed to process events diff --git a/github/github.go b/github/github.go index 26a52da..2b5c933 100644 --- a/github/github.go +++ b/github/github.go @@ -9,7 +9,7 @@ import ( "io/ioutil" "net/http" - "github.com/naiba/webhooks" + "gopkg.in/go-playground/webhooks.v3" ) // Webhook instance contains all methods needed to process events diff --git a/gitlab/gitlab.go b/gitlab/gitlab.go index 5126e1d..cac3a3e 100644 --- a/gitlab/gitlab.go +++ b/gitlab/gitlab.go @@ -6,7 +6,7 @@ import ( "io/ioutil" "net/http" - "github.com/naiba/webhooks" + "gopkg.in/go-playground/webhooks.v3" ) // Webhook instance contains all methods needed to process events diff --git a/gogs/gogs.go b/gogs/gogs.go index d478dee..1cf3798 100644 --- a/gogs/gogs.go +++ b/gogs/gogs.go @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2018, 奶爸<1@5.nu> - * All rights reserved. - */ - package gogs import ( @@ -11,7 +6,7 @@ import ( "io/ioutil" "net/http" - "github.com/naiba/webhooks" + "gopkg.in/go-playground/webhooks.v3" client "github.com/gogits/go-gogs-client" "crypto/hmac" "crypto/sha256" From c9c9d981d78093d243b73c2681752a839f2c662e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=B6=E7=88=B8?= <1@5.nu> Date: Mon, 7 May 2018 18:42:23 +0800 Subject: [PATCH 8/9] godmt --- gogs/gogs.go | 4 ++-- webhooks.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gogs/gogs.go b/gogs/gogs.go index 1cf3798..180c9ce 100644 --- a/gogs/gogs.go +++ b/gogs/gogs.go @@ -6,11 +6,11 @@ import ( "io/ioutil" "net/http" - "gopkg.in/go-playground/webhooks.v3" - client "github.com/gogits/go-gogs-client" "crypto/hmac" "crypto/sha256" "encoding/hex" + client "github.com/gogits/go-gogs-client" + "gopkg.in/go-playground/webhooks.v3" ) // Webhook instance contains all methods needed to process events diff --git a/webhooks.go b/webhooks.go index 994f748..4ab78b1 100644 --- a/webhooks.go +++ b/webhooks.go @@ -28,7 +28,7 @@ func (p Provider) String() string { // webhooks available providers const ( - GitHub Provider = iota + GitHub Provider = iota Bitbucket GitLab Gogs From 3e1bb69b7dd211d0a77a3ec75f162fd6bb0ee1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=B6=E7=88=B8?= <1@5.nu> Date: Tue, 8 May 2018 22:09:43 +0800 Subject: [PATCH 9/9] fix merge issue --- bitbucket/bitbucket.go | 2 +- gogs/gogs.go | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/bitbucket/bitbucket.go b/bitbucket/bitbucket.go index 4fa5929..680582b 100644 --- a/bitbucket/bitbucket.go +++ b/bitbucket/bitbucket.go @@ -43,7 +43,7 @@ const ( PullRequestDeclinedEvent Event = "pullrequest:rejected" PullRequestCommentCreatedEvent Event = "pullrequest:comment_created" PullRequestCommentUpdatedEvent Event = "pullrequest:comment_updated" - PullRequestCommentDeletedEvent Event = "pull_request:comment_deleted" + PullRequestCommentDeletedEvent Event = "pullrequest:comment_deleted" ) // New creates and returns a WebHook instance denoted by the Provider type diff --git a/gogs/gogs.go b/gogs/gogs.go index 180c9ce..d5d57da 100644 --- a/gogs/gogs.go +++ b/gogs/gogs.go @@ -108,10 +108,6 @@ func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) { expectedMAC := hex.EncodeToString(mac.Sum(nil)) if !hmac.Equal([]byte(signature), []byte(expectedMAC)) { - webhooks.DefaultLog.Error("HMAC verification failed") - webhooks.DefaultLog.Debug("LocalHMAC:" + expectedMAC) - webhooks.DefaultLog.Debug("RemoteHMAC:" + signature) - webhooks.DefaultLog.Debug("Secret:" + hook.secret) webhooks.DefaultLog.Debug(string(payload)) http.Error(w, "403 Forbidden - HMAC verification failed", http.StatusForbidden) return