• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

go-playground / webhooks / 5038313099

21 May 2023 03:05PM UTC coverage: 89.667% (-0.2%) from 89.82%
5038313099

push

github

GitHub
Gitlab deployment event (#156)

7 of 7 new or added lines in 1 file covered. (100.0%)

755 of 842 relevant lines covered (89.67%)

9.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.14
/gitlab/gitlab.go
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
        MergeRequestEvents       Event  = "Merge Request Hook"
34
        WikiPageEvents           Event  = "Wiki Page Hook"
35
        PipelineEvents           Event  = "Pipeline Hook"
36
        BuildEvents              Event  = "Build Hook"
37
        JobEvents                Event  = "Job Hook"
38
  DeploymentEvents         Event = "Deployment Hook"
39
        SystemHookEvents         Event  = "System Hook"
40
        objectPush               string = "push"
41
        objectTag                string = "tag_push"
42
        objectMergeRequest       string = "merge_request"
43
        objectBuild              string = "build"
44
        eventProjectCreate       string = "project_create"
45
        eventProjectDestroy      string = "project_destroy"
46
        eventProjectRename       string = "project_rename"
47
        eventProjectTransfer     string = "project_transfer"
48
        eventProjectUpdate       string = "project_update"
49
        eventUserAddToTeam       string = "user_add_to_team"
50
        eventUserRemoveFromTeam  string = "user_remove_from_team"
51
        eventUserUpdateForTeam   string = "user_update_for_team"
52
        eventUserCreate          string = "user_create"
53
        eventUserDestroy         string = "user_destroy"
54
        eventUserFailedLogin     string = "user_failed_login"
55
        eventUserRename          string = "user_rename"
56
        eventKeyCreate           string = "key_create"
57
        eventKeyDestroy          string = "key_destroy"
58
        eventGroupCreate         string = "group_create"
59
        eventGroupDestroy        string = "group_destroy"
60
        eventGroupRename         string = "group_rename"
61
        eventUserAddToGroup      string = "user_add_to_group"
62
        eventUserRemoveFromGroup string = "user_remove_from_group"
63
        eventUserUpdateForGroup  string = "user_update_for_group"
64
)
65

66
// Option is a configuration option for the webhook
67
type Option func(*Webhook) error
68

69
// Options is a namespace var for configuration options
70
var Options = WebhookOptions{}
71

72
// WebhookOptions is a namespace for configuration option methods
73
type WebhookOptions struct{}
74

75
// Secret registers the GitLab secret
76
func (WebhookOptions) Secret(secret string) Option {
1✔
77
        return func(hook *Webhook) error {
2✔
78
                // already convert here to prevent timing attack (conversion depends on secret)
1✔
79
                hash := sha512.Sum512([]byte(secret))
1✔
80
                hook.secretHash = hash[:]
1✔
81
                return nil
1✔
82
        }
1✔
83
}
84

85
// Webhook instance contains all methods needed to process events
86
type Webhook struct {
87
        secretHash []byte
88
}
89

90
// Event defines a GitLab hook event type by the X-Gitlab-Event Header
91
type Event string
92

93
// New creates and returns a WebHook instance denoted by the Provider type
94
func New(options ...Option) (*Webhook, error) {
1✔
95
        hook := new(Webhook)
1✔
96
        for _, opt := range options {
2✔
97
                if err := opt(hook); err != nil {
1✔
98
                        return nil, errors.New("Error applying Option")
×
99
                }
×
100
        }
101
        return hook, nil
1✔
102
}
103

104
// Parse verifies and parses the events specified and returns the payload object or an error
105
func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
41✔
106
        defer func() {
82✔
107
                _, _ = io.Copy(ioutil.Discard, r.Body)
41✔
108
                _ = r.Body.Close()
41✔
109
        }()
41✔
110

111
        if len(events) == 0 {
41✔
112
                return nil, ErrEventNotSpecifiedToParse
×
113
        }
×
114
        if r.Method != http.MethodPost {
41✔
115
                return nil, ErrInvalidHTTPMethod
×
116
        }
×
117

118
        // If we have a Secret set, we should check in constant time
119
        if len(hook.secretHash) > 0 {
82✔
120
                tokenHash := sha512.Sum512([]byte(r.Header.Get("X-Gitlab-Token")))
41✔
121
                if subtle.ConstantTimeCompare(tokenHash[:], hook.secretHash[:]) == 0 {
44✔
122
                        return nil, ErrGitLabTokenVerificationFailed
3✔
123
                }
3✔
124
        }
125

126
        event := r.Header.Get("X-Gitlab-Event")
38✔
127
        if len(event) == 0 {
38✔
128
                return nil, ErrMissingGitLabEventHeader
×
129
        }
×
130

131
        gitLabEvent := Event(event)
38✔
132

38✔
133
        payload, err := ioutil.ReadAll(r.Body)
38✔
134
        if err != nil || len(payload) == 0 {
39✔
135
                return nil, ErrParsingPayload
1✔
136
        }
1✔
137

138
        return eventParsing(gitLabEvent, events, payload)
37✔
139
}
140

141
func eventParsing(gitLabEvent Event, events []Event, payload []byte) (interface{}, error) {
41✔
142

41✔
143
        var found bool
41✔
144
        for _, evt := range events {
86✔
145
                if evt == gitLabEvent {
86✔
146
                        found = true
41✔
147
                        break
41✔
148
                }
149
        }
150
        // event not defined to be parsed
151
        if !found {
41✔
152
                return nil, ErrEventNotFound
×
153
        }
×
154

155
        switch gitLabEvent {
41✔
156
        case PushEvents:
2✔
157
                var pl PushEventPayload
2✔
158
                err := json.Unmarshal([]byte(payload), &pl)
2✔
159
                return pl, err
2✔
160

161
        case TagEvents:
2✔
162
                var pl TagEventPayload
2✔
163
                err := json.Unmarshal([]byte(payload), &pl)
2✔
164
                return pl, err
2✔
165

166
        case ConfidentialIssuesEvents:
1✔
167
                var pl ConfidentialIssueEventPayload
1✔
168
                err := json.Unmarshal([]byte(payload), &pl)
1✔
169
                return pl, err
1✔
170

171
        case IssuesEvents:
1✔
172
                var pl IssueEventPayload
1✔
173
                err := json.Unmarshal([]byte(payload), &pl)
1✔
174
                return pl, err
1✔
175

176
        case CommentEvents:
4✔
177
                var pl CommentEventPayload
4✔
178
                err := json.Unmarshal([]byte(payload), &pl)
4✔
179
                return pl, err
4✔
180

181
        case MergeRequestEvents:
2✔
182
                var pl MergeRequestEventPayload
2✔
183
                err := json.Unmarshal([]byte(payload), &pl)
2✔
184
                return pl, err
2✔
185

186
        case WikiPageEvents:
1✔
187
                var pl WikiPageEventPayload
1✔
188
                err := json.Unmarshal([]byte(payload), &pl)
1✔
189
                return pl, err
1✔
190

191
        case PipelineEvents:
1✔
192
                var pl PipelineEventPayload
1✔
193
                err := json.Unmarshal([]byte(payload), &pl)
1✔
194
                return pl, err
1✔
195

196
        case BuildEvents:
2✔
197
                var pl BuildEventPayload
2✔
198
                err := json.Unmarshal([]byte(payload), &pl)
2✔
199
                return pl, err
2✔
200

201
        case JobEvents:
1✔
202
                var pl JobEventPayload
1✔
203
                err := json.Unmarshal([]byte(payload), &pl)
1✔
204
                if err != nil {
1✔
205
                        return nil, err
×
206
                }
×
207
                if pl.ObjectKind == objectBuild {
2✔
208
                        return eventParsing(BuildEvents, events, payload)
1✔
209
                }
1✔
210
                return pl, nil
×
211

212
        case DeploymentEvents:
1✔
213
                var pl DeploymentEventPayload
1✔
214
                err := json.Unmarshal([]byte(payload), &pl)
1✔
215
                if err != nil {
1✔
216
                        return nil, err
×
217
                }
×
218
                return pl, nil
1✔
219

220
        case SystemHookEvents:
23✔
221
                var pl SystemHookPayload
23✔
222
                err := json.Unmarshal([]byte(payload), &pl)
23✔
223
                if err != nil {
23✔
224
                        return nil, err
×
225
                }
×
226

227
                switch pl.ObjectKind {
23✔
228
                case objectPush:
×
229
                        return eventParsing(PushEvents, events, payload)
×
230

231
                case objectTag:
×
232
                        return eventParsing(TagEvents, events, payload)
×
233

234
                case objectMergeRequest:
1✔
235
                        return eventParsing(MergeRequestEvents, events, payload)
1✔
236
                default:
22✔
237
                        switch pl.EventName {
22✔
238
                        case objectPush:
1✔
239
                                return eventParsing(PushEvents, events, payload)
1✔
240

241
                        case objectTag:
1✔
242
                                return eventParsing(TagEvents, events, payload)
1✔
243

244
                        case objectMergeRequest:
×
245
                                return eventParsing(MergeRequestEvents, events, payload)
×
246

247
                        case eventProjectCreate:
1✔
248
                                var pl ProjectCreatedEventPayload
1✔
249
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
250
                                return pl, err
1✔
251

252
                        case eventProjectDestroy:
1✔
253
                                var pl ProjectDestroyedEventPayload
1✔
254
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
255
                                return pl, err
1✔
256

257
                        case eventProjectRename:
1✔
258
                                var pl ProjectRenamedEventPayload
1✔
259
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
260
                                return pl, err
1✔
261

262
                        case eventProjectTransfer:
1✔
263
                                var pl ProjectTransferredEventPayload
1✔
264
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
265
                                return pl, err
1✔
266

267
                        case eventProjectUpdate:
1✔
268
                                var pl ProjectUpdatedEventPayload
1✔
269
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
270
                                return pl, err
1✔
271

272
                        case eventUserAddToTeam:
1✔
273
                                var pl TeamMemberAddedEventPayload
1✔
274
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
275
                                return pl, err
1✔
276

277
                        case eventUserRemoveFromTeam:
1✔
278
                                var pl TeamMemberRemovedEventPayload
1✔
279
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
280
                                return pl, err
1✔
281

282
                        case eventUserUpdateForTeam:
1✔
283
                                var pl TeamMemberUpdatedEventPayload
1✔
284
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
285
                                return pl, err
1✔
286

287
                        case eventUserCreate:
1✔
288
                                var pl UserCreatedEventPayload
1✔
289
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
290
                                return pl, err
1✔
291

292
                        case eventUserDestroy:
1✔
293
                                var pl UserRemovedEventPayload
1✔
294
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
295
                                return pl, err
1✔
296

297
                        case eventUserFailedLogin:
1✔
298
                                var pl UserFailedLoginEventPayload
1✔
299
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
300
                                return pl, err
1✔
301

302
                        case eventUserRename:
1✔
303
                                var pl UserRenamedEventPayload
1✔
304
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
305
                                return pl, err
1✔
306

307
                        case eventKeyCreate:
1✔
308
                                var pl KeyAddedEventPayload
1✔
309
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
310
                                return pl, err
1✔
311

312
                        case eventKeyDestroy:
1✔
313
                                var pl KeyRemovedEventPayload
1✔
314
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
315
                                return pl, err
1✔
316

317
                        case eventGroupCreate:
1✔
318
                                var pl GroupCreatedEventPayload
1✔
319
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
320
                                return pl, err
1✔
321

322
                        case eventGroupDestroy:
1✔
323
                                var pl GroupRemovedEventPayload
1✔
324
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
325
                                return pl, err
1✔
326

327
                        case eventGroupRename:
1✔
328
                                var pl GroupRenamedEventPayload
1✔
329
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
330
                                return pl, err
1✔
331

332
                        case eventUserAddToGroup:
1✔
333
                                var pl GroupMemberAddedEventPayload
1✔
334
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
335
                                return pl, err
1✔
336

337
                        case eventUserRemoveFromGroup:
1✔
338
                                var pl GroupMemberRemovedEventPayload
1✔
339
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
340
                                return pl, err
1✔
341

342
                        case eventUserUpdateForGroup:
1✔
343
                                var pl GroupMemberUpdatedEventPayload
1✔
344
                                err := json.Unmarshal([]byte(payload), &pl)
1✔
345
                                return pl, err
1✔
346

347
                        default:
×
348
                                return nil, fmt.Errorf("unknown system hook event %s", gitLabEvent)
×
349
                        }
350
                }
351
        default:
×
352
                return nil, fmt.Errorf("unknown event %s", gitLabEvent)
×
353
        }
354
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc