github.com/go-playground/webhooks/v6@v6.3.0/gogs/gogs.go (about)

     1  package gogs
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  
    11  	"crypto/hmac"
    12  	"crypto/sha256"
    13  	"encoding/hex"
    14  
    15  	client "github.com/gogits/go-gogs-client"
    16  )
    17  
    18  // parse errors
    19  var (
    20  	ErrEventNotSpecifiedToParse   = errors.New("no Event specified to parse")
    21  	ErrInvalidHTTPMethod          = errors.New("invalid HTTP Method")
    22  	ErrMissingGogsEventHeader     = errors.New("missing X-Gogs-Event Header")
    23  	ErrMissingGogsSignatureHeader = errors.New("missing X-Gogs-Signature Header")
    24  	ErrEventNotFound              = errors.New("event not defined to be parsed")
    25  	ErrParsingPayload             = errors.New("error parsing payload")
    26  	ErrHMACVerificationFailed     = errors.New("HMAC verification failed")
    27  )
    28  
    29  // Option is a configuration option for the webhook
    30  type Option func(*Webhook) error
    31  
    32  // Options is a namespace var for configuration options
    33  var Options = WebhookOptions{}
    34  
    35  // WebhookOptions is a namespace for configuration option methods
    36  type WebhookOptions struct{}
    37  
    38  // Secret registers the GitLab secret
    39  func (WebhookOptions) Secret(secret string) Option {
    40  	return func(hook *Webhook) error {
    41  		hook.secret = secret
    42  		return nil
    43  	}
    44  }
    45  
    46  // Webhook instance contains all methods needed to process events
    47  type Webhook struct {
    48  	secret string
    49  }
    50  
    51  // Event defines a Gogs hook event type
    52  type Event string
    53  
    54  // Gogs hook types
    55  const (
    56  	CreateEvent       Event = "create"
    57  	DeleteEvent       Event = "delete"
    58  	ForkEvent         Event = "fork"
    59  	PushEvent         Event = "push"
    60  	IssuesEvent       Event = "issues"
    61  	IssueCommentEvent Event = "issue_comment"
    62  	PullRequestEvent  Event = "pull_request"
    63  	ReleaseEvent      Event = "release"
    64  )
    65  
    66  // New creates and returns a WebHook instance denoted by the Provider type
    67  func New(options ...Option) (*Webhook, error) {
    68  	hook := new(Webhook)
    69  	for _, opt := range options {
    70  		if err := opt(hook); err != nil {
    71  			return nil, errors.New("Error applying Option")
    72  		}
    73  	}
    74  	return hook, nil
    75  }
    76  
    77  // Parse verifies and parses the events specified and returns the payload object or an error
    78  func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
    79  	defer func() {
    80  		_, _ = io.Copy(ioutil.Discard, r.Body)
    81  		_ = r.Body.Close()
    82  	}()
    83  
    84  	if len(events) == 0 {
    85  		return nil, ErrEventNotSpecifiedToParse
    86  	}
    87  	if r.Method != http.MethodPost {
    88  		return nil, ErrInvalidHTTPMethod
    89  	}
    90  
    91  	event := r.Header.Get("X-Gogs-Event")
    92  	if len(event) == 0 {
    93  		return nil, ErrMissingGogsEventHeader
    94  	}
    95  
    96  	gogsEvent := Event(event)
    97  
    98  	var found bool
    99  	for _, evt := range events {
   100  		if evt == gogsEvent {
   101  			found = true
   102  			break
   103  		}
   104  	}
   105  	// event not defined to be parsed
   106  	if !found {
   107  		return nil, ErrEventNotFound
   108  	}
   109  
   110  	payload, err := ioutil.ReadAll(r.Body)
   111  	if err != nil || len(payload) == 0 {
   112  		return nil, ErrParsingPayload
   113  	}
   114  
   115  	// If we have a Secret set, we should check the MAC
   116  	if len(hook.secret) > 0 {
   117  		signature := r.Header.Get("X-Gogs-Signature")
   118  		if len(signature) == 0 {
   119  			return nil, ErrMissingGogsSignatureHeader
   120  		}
   121  
   122  		mac := hmac.New(sha256.New, []byte(hook.secret))
   123  		_, _ = mac.Write(payload)
   124  
   125  		expectedMAC := hex.EncodeToString(mac.Sum(nil))
   126  
   127  		if !hmac.Equal([]byte(signature), []byte(expectedMAC)) {
   128  			return nil, ErrHMACVerificationFailed
   129  		}
   130  	}
   131  
   132  	switch gogsEvent {
   133  	case CreateEvent:
   134  		var pl client.CreatePayload
   135  		err = json.Unmarshal([]byte(payload), &pl)
   136  		return pl, err
   137  
   138  	case ReleaseEvent:
   139  		var pl client.ReleasePayload
   140  		err = json.Unmarshal([]byte(payload), &pl)
   141  		return pl, err
   142  
   143  	case PushEvent:
   144  		var pl client.PushPayload
   145  		err = json.Unmarshal([]byte(payload), &pl)
   146  		return pl, err
   147  
   148  	case DeleteEvent:
   149  		var pl client.DeletePayload
   150  		err = json.Unmarshal([]byte(payload), &pl)
   151  		return pl, err
   152  
   153  	case ForkEvent:
   154  		var pl client.ForkPayload
   155  		err = json.Unmarshal([]byte(payload), &pl)
   156  		return pl, err
   157  
   158  	case IssuesEvent:
   159  		var pl client.IssuesPayload
   160  		err = json.Unmarshal([]byte(payload), &pl)
   161  		return pl, err
   162  
   163  	case IssueCommentEvent:
   164  		var pl client.IssueCommentPayload
   165  		err = json.Unmarshal([]byte(payload), &pl)
   166  		return pl, err
   167  
   168  	case PullRequestEvent:
   169  		var pl client.PullRequestPayload
   170  		err = json.Unmarshal([]byte(payload), &pl)
   171  		return pl, err
   172  
   173  	default:
   174  		return nil, fmt.Errorf("unknown event %s", gogsEvent)
   175  	}
   176  }