Skip to content

Commit 75f0d82

Browse files
author
Dean Karn
authored
Merge pull request #145 from huhudev-git/support-gitea
Add gitea webhook support
2 parents 4f72f9c + 9101d63 commit 75f0d82

20 files changed

+5144
-0
lines changed

gitea/gitea.go

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
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+
}

0 commit comments

Comments
 (0)