-
Notifications
You must be signed in to change notification settings - Fork 4
/
webhook.go
173 lines (152 loc) · 4.81 KB
/
webhook.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package server
import (
"database/sql"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
"strings"
)
// Webhook is an inbound github webhook
type Webhook struct {
WebhookID string
ID string
Event string
Signature string
Payload []byte
}
// Extract the webhook ID from the given url
func extractWebhookID(u *url.URL, WebhookPath string) (string, error) {
path := u.Path
if len(path) < len(WebhookPath) {
return "", fmt.Errorf("Invalid URL path '%s'", path)
}
if !strings.HasPrefix(path, WebhookPath) {
return "", fmt.Errorf("Invalid URL path '%s'", path)
}
webhookID := path[len(WebhookPath)+1:]
return webhookID, nil
}
// Check if the webhook id exists. Return the username
func checkWebhookID(db *sql.DB, qaasHost string, qaasExternalPort string, webhookID string) (string, string, error) {
list, err := getRow(db, qaasHost, qaasExternalPort, webhookID)
if err != nil || len(list) == 0 {
return "", "", fmt.Errorf("Invalid webhook ID '%s'", webhookID)
}
if len(list) > 1 {
return "", "", fmt.Errorf("Invalid database; found multiple webhook with webhook ID '%s'", webhookID)
}
return list[0].Groupname, list[0].Username, nil
}
// Read the payload from the request body
func parseWebhookPayload(req *http.Request) ([]byte, error) {
payload, err := ioutil.ReadAll(req.Body)
return payload, err
}
// Write the payload to a file
func writeWebhookPayloadToFile(payloadDir string, payload []byte, username string) error {
payloadFilename := path.Join(payloadDir, "payload")
err := ioutil.WriteFile(payloadFilename, payload, 0600)
if err != nil {
return err
}
return nil
}
func parseWebhookRequest(req *http.Request) (*Webhook, string, error) {
var webhook *Webhook
var webhookID string
var err error
// Check the method
if !strings.EqualFold(req.Method, "POST") {
return webhook, "", fmt.Errorf("invalid method '%s'", req.Method)
}
// Check the URL path
if !isValidURLPath(req.URL.Path) {
return webhook, "", fmt.Errorf("invalid URL path '%s'", req.URL.Path)
}
// Derive the webhook id (if possible)
webhookID, err = extractWebhookID(req.URL, WebhookPath)
if err != nil {
return webhook, "", fmt.Errorf("invalid webhook id '%s' in URL path", webhookID)
}
if !isValidWebhookID(webhookID) {
return webhook, "", fmt.Errorf("invalid webhook id '%s' in URL path", webhookID)
}
return webhook, webhookID, err
}
// WebhookHandler handles a HTTP POST request containing the webhook payload in its body
func (a *API) WebhookHandler(w http.ResponseWriter, req *http.Request) {
// Parse and validate the request
_, webhookID, err := parseWebhookRequest(req)
if err != nil {
w.WriteHeader(http.StatusNotFound)
fmt.Println(err)
fmt.Fprint(w, "Error 404 - Not found: ", err)
return
}
// Check if webhookID exists
groupname, username, err := checkWebhookID(a.DB, a.QaasHost, a.QaasExternalPort, webhookID)
if err != nil {
w.WriteHeader(http.StatusNotFound)
fmt.Println(err)
fmt.Fprint(w, "Error 404 - Not found: ", err)
return
}
// Parse the webhook payload
var payload []byte
payload, err = parseWebhookPayload(req)
if err != nil {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "Error 404 - Not found: ", err)
return
}
// Create the payload dir
payloadDir := path.Join(a.DataDir, "payloads", username)
err = os.MkdirAll(payloadDir, os.ModePerm)
if err != nil {
w.WriteHeader(http.StatusNotFound)
fmt.Println(err)
fmt.Fprint(w, "Error 404 - Not found: ", err)
return
}
// Write the payload to file
err = writeWebhookPayloadToFile(payloadDir, payload, username)
if err != nil {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "Error 404 - Not found: ", err)
return
}
// Prepare the execution of the script
payloadFilename := path.Join(payloadDir, "payload")
targetPayloadDir := path.Join(a.HomeDir, groupname, username, ".qaas", webhookID)
targetPayloadFilename := path.Join(targetPayloadDir, "payload")
userScriptPathFilename := path.Join(a.HomeDir, groupname, username, ".qaas", webhookID, "script.sh")
executeConfig := executeConfiguration{
privateKeyFilename: a.PrivateKeyFilename,
payloadFilename: payloadFilename,
targetPayloadDir: targetPayloadDir,
targetPayloadFilename: targetPayloadFilename,
userScriptPathFilename: userScriptPathFilename,
username: username,
groupname: groupname,
password: a.RelayNodeTestUserPassword,
relayNodeName: a.RelayNode,
dataDir: a.DataDir,
homeDir: a.HomeDir,
webhookID: webhookID,
payload: payload,
}
// Execute the script
err = ExecuteScript(a.Connector, executeConfig)
if err != nil {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "Error 404 - Not found: ", err)
return
}
// Succes
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "Webhook handled successfully")
return
}