covert bitbucket, gitlab and gogs to new style

This commit is contained in:
Dean Karn
2018-07-25 22:59:10 -07:00
parent 077706a514
commit e452811cf1
7 changed files with 298 additions and 280 deletions
+3 -2
View File
@@ -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
+3 -3
View File
@@ -1,8 +1,8 @@
Library webhooks
================
<img align="right" src="https://raw.githubusercontent.com/go-playground/webhooks/v3/logo.png">![Project status](https://img.shields.io/badge/version-4.1.1-green.svg)
[![Build Status](https://travis-ci.org/go-playground/webhooks.svg?branch=v4)](https://travis-ci.org/go-playground/webhooks)
[![Coverage Status](https://coveralls.io/repos/go-playground/webhooks/badge.svg?branch=v4&service=github)](https://coveralls.io/github/go-playground/webhooks?branch=v3)
<img align="right" src="https://raw.githubusercontent.com/go-playground/webhooks/v5/logo.png">![Project status](https://img.shields.io/badge/version-5.0.0-green.svg)
[![Build Status](https://travis-ci.org/go-playground/webhooks.svg?branch=v5)](https://travis-ci.org/go-playground/webhooks)
[![Coverage Status](https://coveralls.io/repos/go-playground/webhooks/badge.svg?branch=v5&service=github)](https://coveralls.io/github/go-playground/webhooks?branch=v5)
[![Go Report Card](https://goreportcard.com/badge/go-playground/webhooks)](https://goreportcard.com/report/go-playground/webhooks)
[![GoDoc](https://godoc.org/gopkg.in/go-playground/webhooks.v5?status.svg)](https://godoc.org/gopkg.in/go-playground/webhooks.v5)
![License](https://img.shields.io/dub/l/vibe-d.svg)
+101 -97
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 {