Skip to content

Commit

Permalink
Adds support for Bitbucket Server (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
goober authored and Dean Karn committed Mar 31, 2019
1 parent a1051fd commit acf48b9
Show file tree
Hide file tree
Showing 22 changed files with 4,032 additions and 1 deletion.
218 changes: 218 additions & 0 deletions bitbucket-server/bitbucketserver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package bitbucketserver

import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
)

var (
ErrEventNotSpecifiedToParse = errors.New("no Event specified to parse")
ErrInvalidHTTPMethod = errors.New("invalid HTTP Method")
ErrMissingEventKeyHeader = errors.New("missing X-Event-Key Header")
ErrMissingHubSignatureHeader = errors.New("missing X-Hub-Signature Header")
ErrEventNotFound = errors.New("event not defined to be parsed")
ErrParsingPayload = errors.New("error parsing payload")
ErrHMACVerificationFailed = errors.New("HMAC verification failed")
)

type Event string

const (
RepositoryReferenceChangedEvent Event = "repo:refs_changed"
RepositoryModifiedEvent Event = "repo:modified"
RepositoryForkedEvent Event = "repo:forked"
RepositoryCommentAddedEvent Event = "repo:comment:added"
RepositoryCommentEditedEvent Event = "repo:comment:edited"
RepositoryCommentDeletedEvent Event = "repo:comment:deleted"

PullRequestOpenedEvent Event = "pr:opened"
PullRequestModifiedEvent Event = "pr:modified"
PullRequestMergedEvent Event = "pr:merged"
PullRequestDeclinedEvent Event = "pr:declined"
PullRequestDeletedEvent Event = "pr:deleted"

PullRequestReviewerUpdatedEvent Event = "pr:reviewer:updated"
PullRequestReviewerApprovedEvent Event = "pr:reviewer:approved"
PullRequestReviewerUnapprovedEvent Event = "pr:reviewer:unapproved"
PullRequestReviewerNeedsWorkEvent Event = "pr:reviewer:needs_work"

PullRequestCommentAddedEvent Event = "pr:comment:added"
PullRequestCommentEditedEvent Event = "pr:comment:edited"
PullRequestCommentDeletedEvent Event = "pr:comment:deleted"

DiagnosticsPingEvent Event = "diagnostics:ping"
)

// 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 GitHub 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
}

// New creates and returns a WebHook instance denoted by the Provider type
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
}

func (hook *Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
defer func() {
_, _ = io.Copy(ioutil.Discard, r.Body)
_ = r.Body.Close()
}()

if len(events) == 0 {
return nil, ErrEventNotSpecifiedToParse
}

if r.Method != http.MethodPost {
return nil, ErrInvalidHTTPMethod
}

event := r.Header.Get("X-Event-Key")
if event == "" {
return nil, ErrMissingEventKeyHeader
}

bitbucketEvent := Event(event)

var found bool
for _, evt := range events {
if evt == bitbucketEvent {
found = true
break
}
}
// event not defined to be parsed
if !found {
return nil, ErrEventNotFound
}

if bitbucketEvent == DiagnosticsPingEvent {
return DiagnosticsPingPayload{}, nil
}

payload, err := ioutil.ReadAll(r.Body)
if err != nil || len(payload) == 0 {
return nil, ErrParsingPayload
}

if len(hook.secret) > 0 {
signature := r.Header.Get("X-Hub-Signature")
if len(signature) == 0 {
return nil, ErrMissingHubSignatureHeader
}
mac := hmac.New(sha256.New, []byte(hook.secret))
_, _ = mac.Write(payload)
expectedMAC := hex.EncodeToString(mac.Sum(nil))

if !hmac.Equal([]byte(signature[7:]), []byte(expectedMAC)) {
return nil, ErrHMACVerificationFailed
}
}

switch bitbucketEvent {
case RepositoryReferenceChangedEvent:
var pl RepositoryReferenceChangedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case RepositoryModifiedEvent:
var pl RepositoryModifiedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case RepositoryForkedEvent:
var pl RepositoryForkedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case RepositoryCommentAddedEvent:
var pl RepositoryCommentAddedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case RepositoryCommentEditedEvent:
var pl RepositoryCommentEditedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case RepositoryCommentDeletedEvent:
var pl RepositoryCommentDeletedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case PullRequestOpenedEvent:
var pl PullRequestOpenedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case PullRequestModifiedEvent:
var pl PullRequestModifiedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case PullRequestMergedEvent:
var pl PullRequestMergedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case PullRequestDeclinedEvent:
var pl PullRequestDeclinedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case PullRequestDeletedEvent:
var pl PullRequestDeletedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case PullRequestReviewerUpdatedEvent:
var pl PullRequestReviewerUpdatedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case PullRequestReviewerApprovedEvent:
var pl PullRequestReviewerApprovedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case PullRequestReviewerUnapprovedEvent:
var pl PullRequestReviewerUnapprovedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case PullRequestReviewerNeedsWorkEvent:
var pl PullRequestReviewerNeedsWorkPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case PullRequestCommentAddedEvent:
var pl PullRequestCommentAddedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case PullRequestCommentEditedEvent:
var pl PullRequestCommentEditedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
case PullRequestCommentDeletedEvent:
var pl PullRequestCommentDeletedPayload
err = json.Unmarshal([]byte(payload), &pl)
return pl, err
default:
return nil, fmt.Errorf("unknown event %s", bitbucketEvent)
}
}
Loading

0 comments on commit acf48b9

Please sign in to comment.