209 lines
5.4 KiB
Go
209 lines
5.4 KiB
Go
package gitlab
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
)
|
|
|
|
// parse errors
|
|
var (
|
|
ErrEventNotSpecifiedToParse = errors.New("no Event specified to parse")
|
|
ErrInvalidHTTPMethod = errors.New("invalid HTTP Method")
|
|
ErrMissingGitLabEventHeader = errors.New("missing X-Gitlab-Event Header")
|
|
ErrGitLabTokenVerificationFailed = errors.New("X-Gitlab-Token validation failed")
|
|
ErrEventNotFound = errors.New("event not defined to be parsed")
|
|
ErrParsingPayload = errors.New("error parsing payload")
|
|
ErrParsingSystemPayload = errors.New("error parsing system payload")
|
|
// ErrHMACVerificationFailed = errors.New("HMAC verification failed")
|
|
)
|
|
|
|
// GitLab hook types
|
|
const (
|
|
PushEvents Event = "Push Hook"
|
|
TagEvents Event = "Tag Push Hook"
|
|
IssuesEvents Event = "Issue Hook"
|
|
ConfidentialIssuesEvents Event = "Confidential Issue Hook"
|
|
CommentEvents Event = "Note Hook"
|
|
MergeRequestEvents Event = "Merge Request Hook"
|
|
WikiPageEvents Event = "Wiki Page Hook"
|
|
PipelineEvents Event = "Pipeline Hook"
|
|
BuildEvents Event = "Build Hook"
|
|
JobEvents Event = "Job Hook"
|
|
SystemHookEvents Event = "System Hook"
|
|
|
|
objectPush string = "push"
|
|
objectTag string = "tag_push"
|
|
objectMergeRequest string = "merge_request"
|
|
)
|
|
|
|
// 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 GitLab hook event type by the X-Gitlab-Event Header
|
|
type Event 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
|
|
}
|
|
|
|
// 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()
|
|
}()
|
|
|
|
if len(events) == 0 {
|
|
return nil, ErrEventNotSpecifiedToParse
|
|
}
|
|
if r.Method != http.MethodPost {
|
|
return nil, ErrInvalidHTTPMethod
|
|
}
|
|
|
|
// If we have a Secret set, we should check the MAC
|
|
if len(hook.secret) > 0 {
|
|
signature := r.Header.Get("X-Gitlab-Token")
|
|
if signature != hook.secret {
|
|
return nil, ErrGitLabTokenVerificationFailed
|
|
}
|
|
}
|
|
|
|
event := r.Header.Get("X-Gitlab-Event")
|
|
if len(event) == 0 {
|
|
return nil, ErrMissingGitLabEventHeader
|
|
}
|
|
|
|
gitLabEvent := Event(event)
|
|
|
|
payload, err := ioutil.ReadAll(r.Body)
|
|
if err != nil || len(payload) == 0 {
|
|
return nil, ErrParsingPayload
|
|
}
|
|
|
|
return eventParsing(gitLabEvent, events, payload)
|
|
}
|
|
|
|
func eventParsing(gitLabEvent Event, events []Event, payload []byte) (interface{}, error) {
|
|
|
|
var found bool
|
|
for _, evt := range events {
|
|
if evt == gitLabEvent {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
// event not defined to be parsed
|
|
if !found {
|
|
return nil, ErrEventNotFound
|
|
}
|
|
|
|
switch gitLabEvent {
|
|
case PushEvents:
|
|
var pl PushEventPayload
|
|
err := json.Unmarshal([]byte(payload), &pl)
|
|
return pl, err
|
|
|
|
case TagEvents:
|
|
var pl TagEventPayload
|
|
err := json.Unmarshal([]byte(payload), &pl)
|
|
return pl, err
|
|
|
|
case ConfidentialIssuesEvents:
|
|
var pl ConfidentialIssueEventPayload
|
|
err := json.Unmarshal([]byte(payload), &pl)
|
|
return pl, err
|
|
|
|
case IssuesEvents:
|
|
var pl IssueEventPayload
|
|
err := json.Unmarshal([]byte(payload), &pl)
|
|
return pl, err
|
|
|
|
case CommentEvents:
|
|
var pl CommentEventPayload
|
|
err := json.Unmarshal([]byte(payload), &pl)
|
|
return pl, err
|
|
|
|
case MergeRequestEvents:
|
|
var pl MergeRequestEventPayload
|
|
err := json.Unmarshal([]byte(payload), &pl)
|
|
return pl, err
|
|
|
|
case WikiPageEvents:
|
|
var pl WikiPageEventPayload
|
|
err := json.Unmarshal([]byte(payload), &pl)
|
|
return pl, err
|
|
|
|
case PipelineEvents:
|
|
var pl PipelineEventPayload
|
|
err := json.Unmarshal([]byte(payload), &pl)
|
|
return pl, err
|
|
|
|
case BuildEvents:
|
|
var pl BuildEventPayload
|
|
err := json.Unmarshal([]byte(payload), &pl)
|
|
return pl, err
|
|
case JobEvents:
|
|
var p1 JobEventPayload
|
|
err := json.Unmarshal([]byte(payload), &p1)
|
|
return p1, err
|
|
|
|
case SystemHookEvents:
|
|
var pl SystemHookPayload
|
|
err := json.Unmarshal([]byte(payload), &pl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch pl.ObjectKind {
|
|
case objectPush:
|
|
return eventParsing(PushEvents, events, payload)
|
|
case objectTag:
|
|
return eventParsing(TagEvents, events, payload)
|
|
case objectMergeRequest:
|
|
return eventParsing(MergeRequestEvents, events, payload)
|
|
default:
|
|
switch pl.EventName {
|
|
case objectPush:
|
|
return eventParsing(PushEvents, events, payload)
|
|
case objectTag:
|
|
return eventParsing(TagEvents, events, payload)
|
|
case objectMergeRequest:
|
|
return eventParsing(MergeRequestEvents, events, payload)
|
|
default:
|
|
return nil, fmt.Errorf("unknown system hook event %s", gitLabEvent)
|
|
}
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("unknown event %s", gitLabEvent)
|
|
}
|
|
}
|