From 8c3a7b75de5be45a64dc06c4e998817e7b52d6bc Mon Sep 17 00:00:00 2001 From: Carlos Tadeu Panato Junior Date: Fri, 16 Feb 2018 17:29:12 +0100 Subject: [PLT-539] Update incoming webhooks to accept multipart/form-data content (#7873) --- api4/webhook.go | 56 ++++++++++++++++++++++++++++++++--------------- i18n/en.json | 4 ++++ model/incoming_webhook.go | 9 ++++++++ web/web_test.go | 6 +++++ 4 files changed, 57 insertions(+), 18 deletions(-) diff --git a/api4/webhook.go b/api4/webhook.go index 90e32a891..e19f14704 100644 --- a/api4/webhook.go +++ b/api4/webhook.go @@ -8,7 +8,10 @@ import ( "net/http" "strings" + l4g "github.com/alecthomas/log4go" + "github.com/gorilla/mux" + "github.com/gorilla/schema" "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/utils" ) @@ -447,34 +450,40 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) { r.ParseForm() - var payload io.Reader + var err *model.AppError + incomingWebhookPayload := &model.IncomingWebhookRequest{} contentType := r.Header.Get("Content-Type") if strings.Split(contentType, "; ")[0] == "application/x-www-form-urlencoded" { - payload = strings.NewReader(r.FormValue("payload")) - } else { - payload = r.Body - } + payload := strings.NewReader(r.FormValue("payload")) - if c.App.Config().LogSettings.EnableWebhookDebugging { - var err error - payload, err = utils.InfoReader( - payload, - utils.T("api.webhook.incoming.debug"), - ) + incomingWebhookPayload, err = decodePayload(payload) if err != nil { - c.Err = model.NewAppError("incomingWebhook", "api.webhook.incoming.debug.error", nil, err.Error(), http.StatusInternalServerError) + c.Err = err return } - } + } else if strings.HasPrefix(contentType, "multipart/form-data") { + r.ParseMultipartForm(0) - parsedRequest, decodeError := model.IncomingWebhookRequestFromJson(payload) + decoder := schema.NewDecoder() + err := decoder.Decode(incomingWebhookPayload, r.PostForm) - if decodeError != nil { - c.Err = decodeError - return + if err != nil { + c.Err = model.NewAppError("incomingWebhook", "api.webhook.incoming.error", nil, err.Error(), http.StatusBadRequest) + return + } + } else { + incomingWebhookPayload, err = decodePayload(r.Body) + if err != nil { + c.Err = err + return + } + } + + if c.App.Config().LogSettings.EnableWebhookDebugging { + l4g.Debug(utils.T("api.webhook.incoming.debug"), incomingWebhookPayload.ToJson()) } - err := c.App.HandleIncomingWebhook(id, parsedRequest) + err = c.App.HandleIncomingWebhook(id, incomingWebhookPayload) if err != nil { c.Err = err return @@ -499,3 +508,14 @@ func commandWebhook(c *Context, w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") w.Write([]byte("ok")) } + +func decodePayload(payload io.Reader) (*model.IncomingWebhookRequest, *model.AppError) { + decodeError := &model.AppError{} + incomingWebhookPayload, decodeError := model.IncomingWebhookRequestFromJson(payload) + + if decodeError != nil { + return nil, decodeError + } + + return incomingWebhookPayload, nil +} diff --git a/i18n/en.json b/i18n/en.json index de910fca8..6e7d583b5 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -3062,6 +3062,10 @@ "id": "api.webhook.incoming.debug.error", "translation": "Could not read payload of incoming webhook." }, + { + "id": "api.webhook.incoming.error", + "translation": "Could not decode the multipart payload of incoming webhook." + }, { "id": "api.webhook.init.debug", "translation": "Initializing webhook API routes" diff --git a/model/incoming_webhook.go b/model/incoming_webhook.go index b38cfeec0..ca9bd116d 100644 --- a/model/incoming_webhook.go +++ b/model/incoming_webhook.go @@ -204,3 +204,12 @@ func IncomingWebhookRequestFromJson(data io.Reader) (*IncomingWebhookRequest, *A return o, nil } + +func (o *IncomingWebhookRequest) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} diff --git a/web/web_test.go b/web/web_test.go index c8d64c61d..5bc69b697 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -133,6 +133,12 @@ func TestIncomingWebhook(t *testing.T) { if _, err := ApiClient.PostToWebhook("abc123", payload); err == nil { t.Fatal("should have errored - bad hook") } + + payloadMultiPart := "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"username\"\r\n\r\nwebhook-bot\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"text\"\r\n\r\nthis is a test :tada:\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--" + if _, err := ApiClient.DoPost("/hooks/"+hook1.Id, payloadMultiPart, "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"); err != nil { + t.Fatal("should have errored - bad hook") + } + } else { if _, err := ApiClient.PostToWebhook("123", "123"); err == nil { t.Fatal("should have failed - webhooks turned off") -- cgit v1.2.3-1-g7c22