github.com/go-playground/webhooks/v6@v6.3.0/gitlab/gitlab.go (about) 1 package gitlab 2 3 import ( 4 "crypto/sha512" 5 "crypto/subtle" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "net/http" 12 ) 13 14 // parse errors 15 var ( 16 ErrEventNotSpecifiedToParse = errors.New("no Event specified to parse") 17 ErrInvalidHTTPMethod = errors.New("invalid HTTP Method") 18 ErrMissingGitLabEventHeader = errors.New("missing X-Gitlab-Event Header") 19 ErrGitLabTokenVerificationFailed = errors.New("X-Gitlab-Token validation failed") 20 ErrEventNotFound = errors.New("event not defined to be parsed") 21 ErrParsingPayload = errors.New("error parsing payload") 22 ErrParsingSystemPayload = errors.New("error parsing system payload") 23 // ErrHMACVerificationFailed = errors.New("HMAC verification failed") 24 ) 25 26 // GitLab hook types 27 const ( 28 PushEvents Event = "Push Hook" 29 TagEvents Event = "Tag Push Hook" 30 IssuesEvents Event = "Issue Hook" 31 ConfidentialIssuesEvents Event = "Confidential Issue Hook" 32 CommentEvents Event = "Note Hook" 33 ConfidentialCommentEvents Event = "Confidential Note Hook" 34 MergeRequestEvents Event = "Merge Request Hook" 35 WikiPageEvents Event = "Wiki Page Hook" 36 PipelineEvents Event = "Pipeline Hook" 37 BuildEvents Event = "Build Hook" 38 JobEvents Event = "Job Hook" 39 DeploymentEvents Event = "Deployment Hook" 40 SystemHookEvents Event = "System Hook" 41 objectPush string = "push" 42 objectTag string = "tag_push" 43 objectMergeRequest string = "merge_request" 44 objectBuild string = "build" 45 eventProjectCreate string = "project_create" 46 eventProjectDestroy string = "project_destroy" 47 eventProjectRename string = "project_rename" 48 eventProjectTransfer string = "project_transfer" 49 eventProjectUpdate string = "project_update" 50 eventUserAddToTeam string = "user_add_to_team" 51 eventUserRemoveFromTeam string = "user_remove_from_team" 52 eventUserUpdateForTeam string = "user_update_for_team" 53 eventUserCreate string = "user_create" 54 eventUserDestroy string = "user_destroy" 55 eventUserFailedLogin string = "user_failed_login" 56 eventUserRename string = "user_rename" 57 eventKeyCreate string = "key_create" 58 eventKeyDestroy string = "key_destroy" 59 eventGroupCreate string = "group_create" 60 eventGroupDestroy string = "group_destroy" 61 eventGroupRename string = "group_rename" 62 eventUserAddToGroup string = "user_add_to_group" 63 eventUserRemoveFromGroup string = "user_remove_from_group" 64 eventUserUpdateForGroup string = "user_update_for_group" 65 ) 66 67 // Option is a configuration option for the webhook 68 type Option func(*Webhook) error 69 70 // Options is a namespace var for configuration options 71 var Options = WebhookOptions{} 72 73 // WebhookOptions is a namespace for configuration option methods 74 type WebhookOptions struct{} 75 76 // Secret registers the GitLab secret 77 func (WebhookOptions) Secret(secret string) Option { 78 return func(hook *Webhook) error { 79 // already convert here to prevent timing attack (conversion depends on secret) 80 hash := sha512.Sum512([]byte(secret)) 81 hook.secretHash = hash[:] 82 return nil 83 } 84 } 85 86 // Webhook instance contains all methods needed to process events 87 type Webhook struct { 88 secretHash []byte 89 } 90 91 // Event defines a GitLab hook event type by the X-Gitlab-Event Header 92 type Event string 93 94 // New creates and returns a WebHook instance denoted by the Provider type 95 func New(options ...Option) (*Webhook, error) { 96 hook := new(Webhook) 97 for _, opt := range options { 98 if err := opt(hook); err != nil { 99 return nil, errors.New("Error applying Option") 100 } 101 } 102 return hook, nil 103 } 104 105 // Parse verifies and parses the events specified and returns the payload object or an error 106 func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) { 107 defer func() { 108 _, _ = io.Copy(ioutil.Discard, r.Body) 109 _ = r.Body.Close() 110 }() 111 112 if len(events) == 0 { 113 return nil, ErrEventNotSpecifiedToParse 114 } 115 if r.Method != http.MethodPost { 116 return nil, ErrInvalidHTTPMethod 117 } 118 119 // If we have a Secret set, we should check in constant time 120 if len(hook.secretHash) > 0 { 121 tokenHash := sha512.Sum512([]byte(r.Header.Get("X-Gitlab-Token"))) 122 if subtle.ConstantTimeCompare(tokenHash[:], hook.secretHash[:]) == 0 { 123 return nil, ErrGitLabTokenVerificationFailed 124 } 125 } 126 127 event := r.Header.Get("X-Gitlab-Event") 128 if len(event) == 0 { 129 return nil, ErrMissingGitLabEventHeader 130 } 131 132 gitLabEvent := Event(event) 133 134 payload, err := ioutil.ReadAll(r.Body) 135 if err != nil || len(payload) == 0 { 136 return nil, ErrParsingPayload 137 } 138 139 return eventParsing(gitLabEvent, events, payload) 140 } 141 142 func eventParsing(gitLabEvent Event, events []Event, payload []byte) (interface{}, error) { 143 144 var found bool 145 for _, evt := range events { 146 if evt == gitLabEvent { 147 found = true 148 break 149 } 150 } 151 // event not defined to be parsed 152 if !found { 153 return nil, ErrEventNotFound 154 } 155 156 switch gitLabEvent { 157 case PushEvents: 158 var pl PushEventPayload 159 err := json.Unmarshal([]byte(payload), &pl) 160 return pl, err 161 162 case TagEvents: 163 var pl TagEventPayload 164 err := json.Unmarshal([]byte(payload), &pl) 165 return pl, err 166 167 case ConfidentialIssuesEvents: 168 var pl ConfidentialIssueEventPayload 169 err := json.Unmarshal([]byte(payload), &pl) 170 return pl, err 171 172 case IssuesEvents: 173 var pl IssueEventPayload 174 err := json.Unmarshal([]byte(payload), &pl) 175 return pl, err 176 177 case ConfidentialCommentEvents: 178 var pl ConfidentialCommentEventPayload 179 err := json.Unmarshal([]byte(payload), &pl) 180 return pl, err 181 182 case CommentEvents: 183 var pl CommentEventPayload 184 err := json.Unmarshal([]byte(payload), &pl) 185 return pl, err 186 187 case MergeRequestEvents: 188 var pl MergeRequestEventPayload 189 err := json.Unmarshal([]byte(payload), &pl) 190 return pl, err 191 192 case WikiPageEvents: 193 var pl WikiPageEventPayload 194 err := json.Unmarshal([]byte(payload), &pl) 195 return pl, err 196 197 case PipelineEvents: 198 var pl PipelineEventPayload 199 err := json.Unmarshal([]byte(payload), &pl) 200 return pl, err 201 202 case BuildEvents: 203 var pl BuildEventPayload 204 err := json.Unmarshal([]byte(payload), &pl) 205 return pl, err 206 207 case JobEvents: 208 var pl JobEventPayload 209 err := json.Unmarshal([]byte(payload), &pl) 210 if err != nil { 211 return nil, err 212 } 213 if pl.ObjectKind == objectBuild { 214 return eventParsing(BuildEvents, events, payload) 215 } 216 return pl, nil 217 218 case DeploymentEvents: 219 var pl DeploymentEventPayload 220 err := json.Unmarshal([]byte(payload), &pl) 221 if err != nil { 222 return nil, err 223 } 224 return pl, nil 225 226 case SystemHookEvents: 227 var pl SystemHookPayload 228 err := json.Unmarshal([]byte(payload), &pl) 229 if err != nil { 230 return nil, err 231 } 232 233 switch pl.ObjectKind { 234 case objectPush: 235 return eventParsing(PushEvents, events, payload) 236 237 case objectTag: 238 return eventParsing(TagEvents, events, payload) 239 240 case objectMergeRequest: 241 return eventParsing(MergeRequestEvents, events, payload) 242 default: 243 switch pl.EventName { 244 case objectPush: 245 return eventParsing(PushEvents, events, payload) 246 247 case objectTag: 248 return eventParsing(TagEvents, events, payload) 249 250 case objectMergeRequest: 251 return eventParsing(MergeRequestEvents, events, payload) 252 253 case eventProjectCreate: 254 var pl ProjectCreatedEventPayload 255 err := json.Unmarshal([]byte(payload), &pl) 256 return pl, err 257 258 case eventProjectDestroy: 259 var pl ProjectDestroyedEventPayload 260 err := json.Unmarshal([]byte(payload), &pl) 261 return pl, err 262 263 case eventProjectRename: 264 var pl ProjectRenamedEventPayload 265 err := json.Unmarshal([]byte(payload), &pl) 266 return pl, err 267 268 case eventProjectTransfer: 269 var pl ProjectTransferredEventPayload 270 err := json.Unmarshal([]byte(payload), &pl) 271 return pl, err 272 273 case eventProjectUpdate: 274 var pl ProjectUpdatedEventPayload 275 err := json.Unmarshal([]byte(payload), &pl) 276 return pl, err 277 278 case eventUserAddToTeam: 279 var pl TeamMemberAddedEventPayload 280 err := json.Unmarshal([]byte(payload), &pl) 281 return pl, err 282 283 case eventUserRemoveFromTeam: 284 var pl TeamMemberRemovedEventPayload 285 err := json.Unmarshal([]byte(payload), &pl) 286 return pl, err 287 288 case eventUserUpdateForTeam: 289 var pl TeamMemberUpdatedEventPayload 290 err := json.Unmarshal([]byte(payload), &pl) 291 return pl, err 292 293 case eventUserCreate: 294 var pl UserCreatedEventPayload 295 err := json.Unmarshal([]byte(payload), &pl) 296 return pl, err 297 298 case eventUserDestroy: 299 var pl UserRemovedEventPayload 300 err := json.Unmarshal([]byte(payload), &pl) 301 return pl, err 302 303 case eventUserFailedLogin: 304 var pl UserFailedLoginEventPayload 305 err := json.Unmarshal([]byte(payload), &pl) 306 return pl, err 307 308 case eventUserRename: 309 var pl UserRenamedEventPayload 310 err := json.Unmarshal([]byte(payload), &pl) 311 return pl, err 312 313 case eventKeyCreate: 314 var pl KeyAddedEventPayload 315 err := json.Unmarshal([]byte(payload), &pl) 316 return pl, err 317 318 case eventKeyDestroy: 319 var pl KeyRemovedEventPayload 320 err := json.Unmarshal([]byte(payload), &pl) 321 return pl, err 322 323 case eventGroupCreate: 324 var pl GroupCreatedEventPayload 325 err := json.Unmarshal([]byte(payload), &pl) 326 return pl, err 327 328 case eventGroupDestroy: 329 var pl GroupRemovedEventPayload 330 err := json.Unmarshal([]byte(payload), &pl) 331 return pl, err 332 333 case eventGroupRename: 334 var pl GroupRenamedEventPayload 335 err := json.Unmarshal([]byte(payload), &pl) 336 return pl, err 337 338 case eventUserAddToGroup: 339 var pl GroupMemberAddedEventPayload 340 err := json.Unmarshal([]byte(payload), &pl) 341 return pl, err 342 343 case eventUserRemoveFromGroup: 344 var pl GroupMemberRemovedEventPayload 345 err := json.Unmarshal([]byte(payload), &pl) 346 return pl, err 347 348 case eventUserUpdateForGroup: 349 var pl GroupMemberUpdatedEventPayload 350 err := json.Unmarshal([]byte(payload), &pl) 351 return pl, err 352 353 default: 354 return nil, fmt.Errorf("unknown system hook event %s", gitLabEvent) 355 } 356 } 357 default: 358 return nil, fmt.Errorf("unknown event %s", gitLabEvent) 359 } 360 }