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