github.com/go-playground/webhooks/v6@v6.3.0/bitbucket-server/bitbucketserver.go (about) 1 package bitbucketserver 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 var ( 16 ErrEventNotSpecifiedToParse = errors.New("no Event specified to parse") 17 ErrInvalidHTTPMethod = errors.New("invalid HTTP Method") 18 ErrMissingEventKeyHeader = errors.New("missing X-Event-Key Header") 19 ErrMissingHubSignatureHeader = errors.New("missing X-Hub-Signature Header") 20 ErrEventNotFound = errors.New("event not defined to be parsed") 21 ErrParsingPayload = errors.New("error parsing payload") 22 ErrHMACVerificationFailed = errors.New("HMAC verification failed") 23 ) 24 25 type Event string 26 27 const ( 28 RepositoryReferenceChangedEvent Event = "repo:refs_changed" 29 RepositoryModifiedEvent Event = "repo:modified" 30 RepositoryForkedEvent Event = "repo:forked" 31 RepositoryCommentAddedEvent Event = "repo:comment:added" 32 RepositoryCommentEditedEvent Event = "repo:comment:edited" 33 RepositoryCommentDeletedEvent Event = "repo:comment:deleted" 34 35 PullRequestOpenedEvent Event = "pr:opened" 36 PullRequestFromReferenceUpdatedEvent Event = "pr:from_ref_updated" 37 PullRequestModifiedEvent Event = "pr:modified" 38 PullRequestMergedEvent Event = "pr:merged" 39 PullRequestDeclinedEvent Event = "pr:declined" 40 PullRequestDeletedEvent Event = "pr:deleted" 41 42 PullRequestReviewerUpdatedEvent Event = "pr:reviewer:updated" 43 PullRequestReviewerApprovedEvent Event = "pr:reviewer:approved" 44 PullRequestReviewerUnapprovedEvent Event = "pr:reviewer:unapproved" 45 PullRequestReviewerNeedsWorkEvent Event = "pr:reviewer:needs_work" 46 47 PullRequestCommentAddedEvent Event = "pr:comment:added" 48 PullRequestCommentEditedEvent Event = "pr:comment:edited" 49 PullRequestCommentDeletedEvent Event = "pr:comment:deleted" 50 51 DiagnosticsPingEvent Event = "diagnostics:ping" 52 ) 53 54 // Option is a configuration option for the webhook 55 type Option func(*Webhook) error 56 57 // Options is a namespace var for configuration options 58 var Options = WebhookOptions{} 59 60 // WebhookOptions is a namespace for configuration option methods 61 type WebhookOptions struct{} 62 63 // Secret registers the GitHub secret 64 func (WebhookOptions) Secret(secret string) Option { 65 return func(hook *Webhook) error { 66 hook.secret = secret 67 return nil 68 } 69 } 70 71 // Webhook instance contains all methods needed to process events 72 type Webhook struct { 73 secret string 74 } 75 76 // New creates and returns a WebHook instance denoted by the Provider type 77 func New(options ...Option) (*Webhook, error) { 78 hook := new(Webhook) 79 for _, opt := range options { 80 if err := opt(hook); err != nil { 81 return nil, errors.New("Error applying Option") 82 } 83 } 84 return hook, nil 85 } 86 87 func (hook *Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) { 88 defer func() { 89 _, _ = io.Copy(ioutil.Discard, r.Body) 90 _ = r.Body.Close() 91 }() 92 93 if len(events) == 0 { 94 return nil, ErrEventNotSpecifiedToParse 95 } 96 97 if r.Method != http.MethodPost { 98 return nil, ErrInvalidHTTPMethod 99 } 100 101 event := r.Header.Get("X-Event-Key") 102 if event == "" { 103 return nil, ErrMissingEventKeyHeader 104 } 105 106 bitbucketEvent := Event(event) 107 108 var found bool 109 for _, evt := range events { 110 if evt == bitbucketEvent { 111 found = true 112 break 113 } 114 } 115 // event not defined to be parsed 116 if !found { 117 return nil, ErrEventNotFound 118 } 119 120 if bitbucketEvent == DiagnosticsPingEvent { 121 return DiagnosticsPingPayload{}, nil 122 } 123 124 payload, err := ioutil.ReadAll(r.Body) 125 if err != nil || len(payload) == 0 { 126 return nil, ErrParsingPayload 127 } 128 129 if len(hook.secret) > 0 { 130 signature := r.Header.Get("X-Hub-Signature") 131 if len(signature) == 0 { 132 return nil, ErrMissingHubSignatureHeader 133 } 134 mac := hmac.New(sha256.New, []byte(hook.secret)) 135 _, _ = mac.Write(payload) 136 expectedMAC := hex.EncodeToString(mac.Sum(nil)) 137 138 if !hmac.Equal([]byte(signature[7:]), []byte(expectedMAC)) { 139 return nil, ErrHMACVerificationFailed 140 } 141 } 142 143 switch bitbucketEvent { 144 case RepositoryReferenceChangedEvent: 145 var pl RepositoryReferenceChangedPayload 146 err = json.Unmarshal([]byte(payload), &pl) 147 return pl, err 148 case RepositoryModifiedEvent: 149 var pl RepositoryModifiedPayload 150 err = json.Unmarshal([]byte(payload), &pl) 151 return pl, err 152 case RepositoryForkedEvent: 153 var pl RepositoryForkedPayload 154 err = json.Unmarshal([]byte(payload), &pl) 155 return pl, err 156 case RepositoryCommentAddedEvent: 157 var pl RepositoryCommentAddedPayload 158 err = json.Unmarshal([]byte(payload), &pl) 159 return pl, err 160 case RepositoryCommentEditedEvent: 161 var pl RepositoryCommentEditedPayload 162 err = json.Unmarshal([]byte(payload), &pl) 163 return pl, err 164 case RepositoryCommentDeletedEvent: 165 var pl RepositoryCommentDeletedPayload 166 err = json.Unmarshal([]byte(payload), &pl) 167 return pl, err 168 case PullRequestOpenedEvent: 169 var pl PullRequestOpenedPayload 170 err = json.Unmarshal([]byte(payload), &pl) 171 return pl, err 172 case PullRequestFromReferenceUpdatedEvent: 173 var pl PullRequestFromReferenceUpdatedPayload 174 err = json.Unmarshal([]byte(payload), &pl) 175 return pl, err 176 case PullRequestModifiedEvent: 177 var pl PullRequestModifiedPayload 178 err = json.Unmarshal([]byte(payload), &pl) 179 return pl, err 180 case PullRequestMergedEvent: 181 var pl PullRequestMergedPayload 182 err = json.Unmarshal([]byte(payload), &pl) 183 return pl, err 184 case PullRequestDeclinedEvent: 185 var pl PullRequestDeclinedPayload 186 err = json.Unmarshal([]byte(payload), &pl) 187 return pl, err 188 case PullRequestDeletedEvent: 189 var pl PullRequestDeletedPayload 190 err = json.Unmarshal([]byte(payload), &pl) 191 return pl, err 192 case PullRequestReviewerUpdatedEvent: 193 var pl PullRequestReviewerUpdatedPayload 194 err = json.Unmarshal([]byte(payload), &pl) 195 return pl, err 196 case PullRequestReviewerApprovedEvent: 197 var pl PullRequestReviewerApprovedPayload 198 err = json.Unmarshal([]byte(payload), &pl) 199 return pl, err 200 case PullRequestReviewerUnapprovedEvent: 201 var pl PullRequestReviewerUnapprovedPayload 202 err = json.Unmarshal([]byte(payload), &pl) 203 return pl, err 204 case PullRequestReviewerNeedsWorkEvent: 205 var pl PullRequestReviewerNeedsWorkPayload 206 err = json.Unmarshal([]byte(payload), &pl) 207 return pl, err 208 case PullRequestCommentAddedEvent: 209 var pl PullRequestCommentAddedPayload 210 err = json.Unmarshal([]byte(payload), &pl) 211 return pl, err 212 case PullRequestCommentEditedEvent: 213 var pl PullRequestCommentEditedPayload 214 err = json.Unmarshal([]byte(payload), &pl) 215 return pl, err 216 case PullRequestCommentDeletedEvent: 217 var pl PullRequestCommentDeletedPayload 218 err = json.Unmarshal([]byte(payload), &pl) 219 return pl, err 220 default: 221 return nil, fmt.Errorf("unknown event %s", bitbucketEvent) 222 } 223 }