covert bitbucket, gitlab and gogs to new style
This commit is contained in:
+3
-2
@@ -1,6 +1,6 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.10.2
|
||||
- 1.10.3
|
||||
- tip
|
||||
matrix:
|
||||
allow_failures:
|
||||
@@ -22,6 +22,7 @@ before_install:
|
||||
- ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/webhooks.v2
|
||||
- ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/webhooks.v3
|
||||
- ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/webhooks.v4
|
||||
- ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/webhooks.v5
|
||||
|
||||
before_script:
|
||||
- go vet ./...
|
||||
@@ -34,6 +35,6 @@ script:
|
||||
- go test -race
|
||||
|
||||
after_success: |
|
||||
[ $TRAVIS_GO_VERSION = 1.10.2 ] &&
|
||||
[ $TRAVIS_GO_VERSION = 1.10.3 ] &&
|
||||
overalls -project="github.com/go-playground/webhooks" -covermode=count -ignore=.git,examples -debug &&
|
||||
goveralls -coverprofile=overalls.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
Library webhooks
|
||||
================
|
||||
<img align="right" src="https://raw.githubusercontent.com/go-playground/webhooks/v3/logo.png">
|
||||
[](https://travis-ci.org/go-playground/webhooks)
|
||||
[](https://coveralls.io/github/go-playground/webhooks?branch=v3)
|
||||
<img align="right" src="https://raw.githubusercontent.com/go-playground/webhooks/v5/logo.png">
|
||||
[](https://travis-ci.org/go-playground/webhooks)
|
||||
[](https://coveralls.io/github/go-playground/webhooks?branch=v5)
|
||||
[](https://goreportcard.com/report/go-playground/webhooks)
|
||||
[](https://godoc.org/gopkg.in/go-playground/webhooks.v5)
|
||||

|
||||
|
||||
+101
-97
@@ -2,23 +2,27 @@ package bitbucket
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
"gopkg.in/go-playground/webhooks.v5"
|
||||
// parse errros
|
||||
var (
|
||||
ErrEventNotSpecifiedToParse = errors.New("No Event specified to parse")
|
||||
ErrInvalidHTTPMethod = errors.New("Invalid HTTP Method")
|
||||
ErrMissingHookUUIDHeader = errors.New("Missing X-Hook-UUID Header")
|
||||
ErrMissingEventKeyHeader = errors.New("Missing X-Event-Key Header")
|
||||
ErrEventNotFound = errors.New("Event not defined to be parsed")
|
||||
ErrParsingPayload = errors.New("Error parsing payload")
|
||||
ErrUUIDVerificationFailed = errors.New("UUID verification failed")
|
||||
)
|
||||
|
||||
// Webhook instance contains all methods needed to process events
|
||||
type Webhook struct {
|
||||
provider webhooks.Provider
|
||||
uuid string
|
||||
eventFuncs map[Event]webhooks.ProcessPayloadFunc
|
||||
}
|
||||
|
||||
// Config defines the configuration to create a new Bitbucket Webhook instance
|
||||
type Config struct {
|
||||
UUID string
|
||||
uuid string
|
||||
}
|
||||
|
||||
// Event defines a Bitbucket hook event type
|
||||
@@ -46,154 +50,154 @@ const (
|
||||
PullRequestCommentDeletedEvent Event = "pullrequest:comment_deleted"
|
||||
)
|
||||
|
||||
// Option is a configuration option for the webhook
|
||||
type Option func(*Webhook) error
|
||||
|
||||
// Options is a namespace var for configuration options
|
||||
var Options = WebhookOptions{}
|
||||
|
||||
// WebhookOptions is a namespace for configuration option methods
|
||||
type WebhookOptions struct{}
|
||||
|
||||
// UUID registers the BitBucket secret
|
||||
func (WebhookOptions) UUID(uuid string) Option {
|
||||
return func(hook *Webhook) error {
|
||||
hook.uuid = uuid
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// New creates and returns a WebHook instance denoted by the Provider type
|
||||
func New(config *Config) *Webhook {
|
||||
return &Webhook{
|
||||
provider: webhooks.Bitbucket,
|
||||
uuid: config.UUID,
|
||||
eventFuncs: map[Event]webhooks.ProcessPayloadFunc{},
|
||||
func New(options ...Option) (*Webhook, error) {
|
||||
hook := new(Webhook)
|
||||
for _, opt := range options {
|
||||
if err := opt(hook); err != nil {
|
||||
return nil, errors.New("Error applying Option")
|
||||
}
|
||||
}
|
||||
return hook, nil
|
||||
}
|
||||
|
||||
// Provider returns the current hooks provider ID
|
||||
func (hook Webhook) Provider() webhooks.Provider {
|
||||
return hook.provider
|
||||
}
|
||||
// Parse verifies and parses the events specified and returns the payload object or an error
|
||||
func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
|
||||
defer func() {
|
||||
_, _ = io.Copy(ioutil.Discard, r.Body)
|
||||
_ = r.Body.Close()
|
||||
}()
|
||||
|
||||
// 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
|
||||
if len(events) == 0 {
|
||||
return nil, ErrEventNotSpecifiedToParse
|
||||
}
|
||||
if r.Method != http.MethodPost {
|
||||
return nil, ErrInvalidHTTPMethod
|
||||
}
|
||||
}
|
||||
|
||||
// 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...")
|
||||
|
||||
uuid := r.Header.Get("X-Hook-UUID")
|
||||
if uuid == "" {
|
||||
webhooks.DefaultLog.Error("Missing X-Hook-UUID Header")
|
||||
http.Error(w, "400 Bad Request - Missing X-Hook-UUID Header", http.StatusBadRequest)
|
||||
return
|
||||
return nil, ErrMissingHookUUIDHeader
|
||||
}
|
||||
webhooks.DefaultLog.Debug(fmt.Sprintf("X-Hook-UUID:%s", uuid))
|
||||
|
||||
if len(hook.uuid) > 0 {
|
||||
if uuid != hook.uuid {
|
||||
webhooks.DefaultLog.Error(fmt.Sprintf("X-Hook-UUID %s does not match configured uuid of %s", uuid, hook.uuid))
|
||||
http.Error(w, "403 Forbidden - X-Hook-UUID does not match", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
webhooks.DefaultLog.Debug("hook uuid not defined - recommend setting for improved security")
|
||||
if len(hook.uuid) > 0 && uuid != hook.uuid {
|
||||
return nil, ErrUUIDVerificationFailed
|
||||
}
|
||||
|
||||
event := r.Header.Get("X-Event-Key")
|
||||
if event == "" {
|
||||
webhooks.DefaultLog.Error("Missing X-Event-Key Header")
|
||||
http.Error(w, "400 Bad Request - Missing X-Event-Key Header", http.StatusBadRequest)
|
||||
return
|
||||
return nil, ErrMissingEventKeyHeader
|
||||
}
|
||||
webhooks.DefaultLog.Debug(fmt.Sprintf("X-Event-Key:%s", event))
|
||||
|
||||
bitbucketEvent := Event(event)
|
||||
|
||||
fn, ok := hook.eventFuncs[bitbucketEvent]
|
||||
// if no event registered
|
||||
if !ok {
|
||||
webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in bitbucket that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event))
|
||||
return
|
||||
var found bool
|
||||
for _, evt := range events {
|
||||
if evt == bitbucketEvent {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// event not defined to be parsed
|
||||
if !found {
|
||||
return nil, ErrEventNotFound
|
||||
}
|
||||
|
||||
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
|
||||
return nil, ErrParsingPayload
|
||||
}
|
||||
webhooks.DefaultLog.Debug(fmt.Sprintf("Payload:%s", string(payload)))
|
||||
hd := webhooks.Header(r.Header)
|
||||
|
||||
switch bitbucketEvent {
|
||||
case RepoPushEvent:
|
||||
var pl RepoPushPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case RepoForkEvent:
|
||||
var pl RepoForkPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case RepoUpdatedEvent:
|
||||
var pl RepoUpdatedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case RepoCommitCommentCreatedEvent:
|
||||
var pl RepoCommitCommentCreatedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case RepoCommitStatusCreatedEvent:
|
||||
var pl RepoCommitStatusCreatedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case RepoCommitStatusUpdatedEvent:
|
||||
var pl RepoCommitStatusUpdatedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case IssueCreatedEvent:
|
||||
var pl IssueCreatedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case IssueUpdatedEvent:
|
||||
var pl IssueUpdatedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case IssueCommentCreatedEvent:
|
||||
var pl IssueCommentCreatedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case PullRequestCreatedEvent:
|
||||
var pl PullRequestCreatedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case PullRequestUpdatedEvent:
|
||||
var pl PullRequestUpdatedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case PullRequestApprovedEvent:
|
||||
var pl PullRequestApprovedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case PullRequestUnapprovedEvent:
|
||||
var pl PullRequestUnapprovedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case PullRequestMergedEvent:
|
||||
var pl PullRequestMergedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case PullRequestDeclinedEvent:
|
||||
var pl PullRequestDeclinedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case PullRequestCommentCreatedEvent:
|
||||
var pl PullRequestCommentCreatedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case PullRequestCommentUpdatedEvent:
|
||||
var pl PullRequestCommentUpdatedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
case PullRequestCommentDeletedEvent:
|
||||
var pl PullRequestCommentDeletedPayload
|
||||
json.Unmarshal([]byte(payload), &pl)
|
||||
hook.runProcessPayloadFunc(fn, pl, hd)
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown event %s", bitbucketEvent)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
+3
-3
@@ -17,9 +17,9 @@ var (
|
||||
ErrEventNotSpecifiedToParse = errors.New("No Event specified to parse")
|
||||
ErrInvalidHTTPMethod = errors.New("Invalid HTTP Method")
|
||||
ErrMissingGithubEventHeader = errors.New("Missing X-GitHub-Event Header")
|
||||
ErrMissingHubSignatureHeader = errors.New("Missing X-Hub-Signature")
|
||||
ErrMissingHubSignatureHeader = errors.New("Missing X-Hub-Signature Header")
|
||||
ErrEventNotFound = errors.New("Event not defined to be parsed")
|
||||
ErrParsingPayload = errors.New("Error Reading Payload")
|
||||
ErrParsingPayload = errors.New("Error parsing payload")
|
||||
ErrHMACVerificationFailed = errors.New("HMAC verification failed")
|
||||
)
|
||||
|
||||
@@ -123,7 +123,7 @@ func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error)
|
||||
}
|
||||
|
||||
event := r.Header.Get("X-GitHub-Event")
|
||||
if len(event) == 0 {
|
||||
if event == "" {
|
||||
return nil, ErrMissingGithubEventHeader
|
||||
}
|
||||
gitHubEvent := Event(event)
|
||||
|
||||
+97
-88
@@ -2,28 +2,13 @@ package gitlab
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"gopkg.in/go-playground/webhooks.v5"
|
||||
)
|
||||
|
||||
// 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 GitHub Webhook instance
|
||||
type Config struct {
|
||||
Secret string
|
||||
}
|
||||
|
||||
// Event defines a GitHub hook event type
|
||||
type Event string
|
||||
|
||||
// GitLab hook types
|
||||
const (
|
||||
PushEvents Event = "Push Hook"
|
||||
@@ -37,121 +22,145 @@ const (
|
||||
BuildEvents Event = "Build Hook"
|
||||
)
|
||||
|
||||
// Option is a configuration option for the webhook
|
||||
type Option func(*Webhook) error
|
||||
|
||||
// Options is a namespace var for configuration options
|
||||
var Options = WebhookOptions{}
|
||||
|
||||
// WebhookOptions is a namespace for configuration option methods
|
||||
type WebhookOptions struct{}
|
||||
|
||||
// Secret registers the GitLab secret
|
||||
func (WebhookOptions) Secret(secret string) Option {
|
||||
return func(hook *Webhook) error {
|
||||
hook.secret = secret
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Webhook instance contains all methods needed to process events
|
||||
type Webhook struct {
|
||||
secret string
|
||||
}
|
||||
|
||||
// Event defines a GitHub hook event type
|
||||
type Event string
|
||||
|
||||
// New creates and returns a WebHook instance denoted by the Provider type
|
||||
func New(config *Config) *Webhook {
|
||||
return &Webhook{
|
||||
provider: webhooks.GitLab,
|
||||
secret: config.Secret,
|
||||
eventFuncs: map[Event]webhooks.ProcessPayloadFunc{},
|
||||
func New(options ...Option) (*Webhook, error) {
|
||||
hook := new(Webhook)
|
||||
for _, opt := range options {
|
||||
if err := opt(hook); err != nil {
|
||||
return nil, errors.New("Error applying Option")
|
||||
}
|
||||
}
|
||||
return hook, nil
|
||||
}
|
||||
|
||||
// Provider returns the current hooks provider ID
|
||||
func (hook Webhook) Provider() webhooks.Provider {
|
||||
return hook.provider
|
||||
}
|
||||
// parse errros
|
||||
var (
|
||||
ErrEventNotSpecifiedToParse = errors.New("No Event specified to parse")
|
||||
ErrInvalidHTTPMethod = errors.New("Invalid HTTP Method")
|
||||
ErrMissingGitLabEventHeader = errors.New("Missing X-Gitlab-Event Header")
|
||||
ErrMissingGitLabTokenHeader = errors.New("Missing X-Gitlab-Token Header")
|
||||
ErrEventNotFound = errors.New("Event not defined to be parsed")
|
||||
ErrParsingPayload = errors.New("Error parsing payload")
|
||||
// ErrHMACVerificationFailed = errors.New("HMAC verification failed")
|
||||
)
|
||||
|
||||
// RegisterEvents registers the function to call when the specified event(s) are encountered
|
||||
func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) {
|
||||
// Parse verifies and parses the events specified and returns the payload object or an error
|
||||
func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
|
||||
defer func() {
|
||||
_, _ = io.Copy(ioutil.Discard, r.Body)
|
||||
_ = r.Body.Close()
|
||||
}()
|
||||
|
||||
for _, event := range events {
|
||||
hook.eventFuncs[event] = fn
|
||||
if len(events) == 0 {
|
||||
return nil, ErrEventNotSpecifiedToParse
|
||||
}
|
||||
if r.Method != http.MethodPost {
|
||||
return nil, ErrInvalidHTTPMethod
|
||||
}
|
||||
}
|
||||
|
||||
// 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-Gitlab-Event")
|
||||
if len(event) == 0 {
|
||||
webhooks.DefaultLog.Error("Missing X-Gitlab-Event Header")
|
||||
http.Error(w, "400 Bad Request - Missing X-Gitlab-Event Header", http.StatusBadRequest)
|
||||
return
|
||||
return nil, ErrMissingGitLabEventHeader
|
||||
}
|
||||
webhooks.DefaultLog.Debug(fmt.Sprintf("X-Gitlab-Event:%s", event))
|
||||
|
||||
gitLabEvent := Event(event)
|
||||
|
||||
fn, ok := hook.eventFuncs[gitLabEvent]
|
||||
// if no event registered
|
||||
if !ok {
|
||||
webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in gitlab that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event))
|
||||
return
|
||||
var found bool
|
||||
for _, evt := range events {
|
||||
if evt == gitLabEvent {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// event not defined to be parsed
|
||||
if !found {
|
||||
return nil, ErrEventNotFound
|
||||
}
|
||||
|
||||
payload, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil || len(payload) == 0 {
|
||||
webhooks.DefaultLog.Error("Issue reading Payload")
|
||||
http.Error(w, "Error reading Payload", http.StatusInternalServerError)
|
||||
return
|
||||
return nil, ErrParsingPayload
|
||||
}
|
||||
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-Gitlab-Token")
|
||||
if signature != hook.secret {
|
||||
webhooks.DefaultLog.Error(fmt.Sprintf("Invalid X-Gitlab-Token of '%s'", signature))
|
||||
http.Error(w, "403 Forbidden - Token missmatch", http.StatusForbidden)
|
||||
return
|
||||
return nil, ErrMissingGitLabTokenHeader
|
||||
}
|
||||
}
|
||||
|
||||
// Make headers available to ProcessPayloadFunc as a webhooks type
|
||||
hd := webhooks.Header(r.Header)
|
||||
|
||||
switch gitLabEvent {
|
||||
case PushEvents:
|
||||
var pe PushEventPayload
|
||||
json.Unmarshal([]byte(payload), &pe)
|
||||
hook.runProcessPayloadFunc(fn, pe, hd)
|
||||
var pl PushEventPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case TagEvents:
|
||||
var te TagEventPayload
|
||||
json.Unmarshal([]byte(payload), &te)
|
||||
hook.runProcessPayloadFunc(fn, te, hd)
|
||||
var pl TagEventPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case ConfidentialIssuesEvents:
|
||||
var cie ConfidentialIssueEventPayload
|
||||
json.Unmarshal([]byte(payload), &cie)
|
||||
hook.runProcessPayloadFunc(fn, cie, hd)
|
||||
var pl ConfidentialIssueEventPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case IssuesEvents:
|
||||
var ie IssueEventPayload
|
||||
json.Unmarshal([]byte(payload), &ie)
|
||||
hook.runProcessPayloadFunc(fn, ie, hd)
|
||||
var pl IssueEventPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case CommentEvents:
|
||||
var ce CommentEventPayload
|
||||
json.Unmarshal([]byte(payload), &ce)
|
||||
hook.runProcessPayloadFunc(fn, ce, hd)
|
||||
var pl CommentEventPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case MergeRequestEvents:
|
||||
var mre MergeRequestEventPayload
|
||||
json.Unmarshal([]byte(payload), &mre)
|
||||
hook.runProcessPayloadFunc(fn, mre, hd)
|
||||
var pl MergeRequestEventPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case WikiPageEvents:
|
||||
var wpe WikiPageEventPayload
|
||||
json.Unmarshal([]byte(payload), &wpe)
|
||||
hook.runProcessPayloadFunc(fn, wpe, hd)
|
||||
var pl WikiPageEventPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case PipelineEvents:
|
||||
var pe PipelineEventPayload
|
||||
json.Unmarshal([]byte(payload), &pe)
|
||||
hook.runProcessPayloadFunc(fn, pe, hd)
|
||||
var pl PipelineEventPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case BuildEvents:
|
||||
var be BuildEventPayload
|
||||
json.Unmarshal([]byte(payload), &be)
|
||||
hook.runProcessPayloadFunc(fn, be, hd)
|
||||
var pl BuildEventPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown event %s", gitLabEvent)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
+89
-81
@@ -2,7 +2,9 @@ package gogs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
@@ -11,19 +13,28 @@ import (
|
||||
"encoding/hex"
|
||||
|
||||
client "github.com/gogits/go-gogs-client"
|
||||
"gopkg.in/go-playground/webhooks.v5"
|
||||
)
|
||||
|
||||
// Option is a configuration option for the webhook
|
||||
type Option func(*Webhook) error
|
||||
|
||||
// Options is a namespace var for configuration options
|
||||
var Options = WebhookOptions{}
|
||||
|
||||
// WebhookOptions is a namespace for configuration option methods
|
||||
type WebhookOptions struct{}
|
||||
|
||||
// Secret registers the GitLab secret
|
||||
func (WebhookOptions) Secret(secret string) Option {
|
||||
return func(hook *Webhook) error {
|
||||
hook.secret = secret
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
secret string
|
||||
}
|
||||
|
||||
// Event defines a Gogs hook event type
|
||||
@@ -42,66 +53,71 @@ const (
|
||||
)
|
||||
|
||||
// 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{},
|
||||
func New(options ...Option) (*Webhook, error) {
|
||||
hook := new(Webhook)
|
||||
for _, opt := range options {
|
||||
if err := opt(hook); err != nil {
|
||||
return nil, errors.New("Error applying Option")
|
||||
}
|
||||
}
|
||||
return hook, nil
|
||||
}
|
||||
|
||||
// Provider returns the current hooks provider ID
|
||||
func (hook Webhook) Provider() webhooks.Provider {
|
||||
return hook.provider
|
||||
}
|
||||
// parse errros
|
||||
var (
|
||||
ErrEventNotSpecifiedToParse = errors.New("No Event specified to parse")
|
||||
ErrInvalidHTTPMethod = errors.New("Invalid HTTP Method")
|
||||
ErrMissingGogsEventHeader = errors.New("Missing X-Gogs-Event Header")
|
||||
ErrMissingGogsSignatureHeader = errors.New("Missing X-Gogs-Signature Header")
|
||||
ErrEventNotFound = errors.New("Event not defined to be parsed")
|
||||
ErrParsingPayload = errors.New("Error parsing payload")
|
||||
ErrHMACVerificationFailed = errors.New("HMAC verification failed")
|
||||
)
|
||||
|
||||
// RegisterEvents registers the function to call when the specified event(s) are encountered
|
||||
func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) {
|
||||
// Parse verifies and parses the events specified and returns the payload object or an error
|
||||
func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
|
||||
defer func() {
|
||||
_, _ = io.Copy(ioutil.Discard, r.Body)
|
||||
_ = r.Body.Close()
|
||||
}()
|
||||
|
||||
for _, event := range events {
|
||||
hook.eventFuncs[event] = fn
|
||||
if len(events) == 0 {
|
||||
return nil, ErrEventNotSpecifiedToParse
|
||||
}
|
||||
if r.Method != http.MethodPost {
|
||||
return nil, ErrInvalidHTTPMethod
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
return nil, ErrMissingGogsEventHeader
|
||||
}
|
||||
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
|
||||
var found bool
|
||||
for _, evt := range events {
|
||||
if evt == gogsEvent {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// event not defined to be parsed
|
||||
if !found {
|
||||
return nil, ErrEventNotFound
|
||||
}
|
||||
|
||||
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
|
||||
return nil, ErrParsingPayload
|
||||
}
|
||||
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
|
||||
return nil, ErrMissingGogsSignatureHeader
|
||||
}
|
||||
webhooks.DefaultLog.Debug(fmt.Sprintf("X-Gogs-Signature:%s", signature))
|
||||
|
||||
mac := hmac.New(sha256.New, []byte(hook.secret))
|
||||
mac.Write(payload)
|
||||
@@ -109,60 +125,52 @@ 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.Debug(string(payload))
|
||||
http.Error(w, "403 Forbidden - HMAC verification failed", http.StatusForbidden)
|
||||
return
|
||||
return nil, ErrHMACVerificationFailed
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
var pl client.CreatePayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case ReleaseEvent:
|
||||
var re client.ReleasePayload
|
||||
json.Unmarshal([]byte(payload), &re)
|
||||
hook.runProcessPayloadFunc(fn, re, hd)
|
||||
var pl client.ReleasePayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case PushEvent:
|
||||
var pe client.PushPayload
|
||||
json.Unmarshal([]byte(payload), &pe)
|
||||
hook.runProcessPayloadFunc(fn, pe, hd)
|
||||
var pl client.PushPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case DeleteEvent:
|
||||
var de client.DeletePayload
|
||||
json.Unmarshal([]byte(payload), &de)
|
||||
hook.runProcessPayloadFunc(fn, de, hd)
|
||||
var pl client.DeletePayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case ForkEvent:
|
||||
var fe client.ForkPayload
|
||||
json.Unmarshal([]byte(payload), &fe)
|
||||
hook.runProcessPayloadFunc(fn, fe, hd)
|
||||
var pl client.ForkPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case IssuesEvent:
|
||||
var ie client.IssuesPayload
|
||||
json.Unmarshal([]byte(payload), &ie)
|
||||
hook.runProcessPayloadFunc(fn, ie, hd)
|
||||
var pl client.IssuesPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case IssueCommentEvent:
|
||||
var ice client.IssueCommentPayload
|
||||
json.Unmarshal([]byte(payload), &ice)
|
||||
hook.runProcessPayloadFunc(fn, ice, hd)
|
||||
var pl client.IssueCommentPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
case PullRequestEvent:
|
||||
var pre client.PullRequestPayload
|
||||
json.Unmarshal([]byte(payload), &pre)
|
||||
hook.runProcessPayloadFunc(fn, pre, hd)
|
||||
var pl client.PullRequestPayload
|
||||
err = json.Unmarshal([]byte(payload), &pl)
|
||||
return pl, err
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown event %s", gogsEvent)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
+2
-6
@@ -1,11 +1,7 @@
|
||||
package webhooks
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Header provides http.Header to minimize imports
|
||||
type Header http.Header
|
||||
// type Header http.Header
|
||||
|
||||
// // Provider defines the type of webhook
|
||||
// type Provider int
|
||||
@@ -46,7 +42,7 @@ type Header http.Header
|
||||
// }
|
||||
|
||||
// ProcessPayloadFunc is a common function for payload return values
|
||||
type ProcessPayloadFunc func(payload interface{}, header Header) error
|
||||
// type ProcessPayloadFunc func(payload interface{}, header Header) error
|
||||
|
||||
// // Handler returns the webhook http.Handler for use in your own Mux implementation
|
||||
// func Handler(hook Webhook) http.Handler {
|
||||
|
||||
Reference in New Issue
Block a user