mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
* Content-Type is optional mime.ParseMimeType returns and "no media type" error if the passed string is empty. Given that the Content-Type header is optional we shouldn't return an error in that case, so we're fixing that allowing the users to call the webhook without passing that header * Include webhook id in the error message Given that the number of webhooks could be big the user could need the id to check which one of the multiple webhooks are failing so include the id aids in that part
127 lines
3.2 KiB
Go
127 lines
3.2 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package web
|
|
|
|
import (
|
|
"io"
|
|
"mime"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/gorilla/schema"
|
|
|
|
"github.com/mattermost/mattermost-server/v5/mlog"
|
|
"github.com/mattermost/mattermost-server/v5/model"
|
|
)
|
|
|
|
func (w *Web) InitWebhooks() {
|
|
w.MainRouter.Handle("/hooks/commands/{id:[A-Za-z0-9]+}", w.NewHandler(commandWebhook)).Methods("POST")
|
|
w.MainRouter.Handle("/hooks/{id:[A-Za-z0-9]+}", w.NewHandler(incomingWebhook)).Methods("POST")
|
|
}
|
|
|
|
func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
params := mux.Vars(r)
|
|
id := params["id"]
|
|
|
|
r.ParseForm()
|
|
|
|
var err *model.AppError
|
|
var mediaType string
|
|
incomingWebhookPayload := &model.IncomingWebhookRequest{}
|
|
contentType := r.Header.Get("Content-Type")
|
|
// Content-Type header is optional so could be empty
|
|
if contentType != "" {
|
|
var mimeErr error
|
|
mediaType, _, mimeErr = mime.ParseMediaType(contentType)
|
|
if mimeErr != nil && mimeErr != mime.ErrInvalidMediaParameter {
|
|
c.Err = model.NewAppError("incomingWebhook",
|
|
"api.webhook.incoming.error",
|
|
nil,
|
|
"webhook_id="+id+", error: "+mimeErr.Error(),
|
|
http.StatusBadRequest,
|
|
)
|
|
return
|
|
}
|
|
}
|
|
|
|
defer func() {
|
|
if *c.App.Config().LogSettings.EnableWebhookDebugging {
|
|
if c.Err != nil {
|
|
mlog.Debug("Incoming webhook received", mlog.String("webhook_id", id), mlog.String("request_id", c.App.RequestId()), mlog.String("payload", incomingWebhookPayload.ToJson()))
|
|
}
|
|
}
|
|
}()
|
|
|
|
if mediaType == "application/x-www-form-urlencoded" {
|
|
payload := strings.NewReader(r.FormValue("payload"))
|
|
|
|
incomingWebhookPayload, err = decodePayload(payload)
|
|
if err != nil {
|
|
c.Err = err
|
|
return
|
|
}
|
|
} else if mediaType == "multipart/form-data" {
|
|
r.ParseMultipartForm(0)
|
|
|
|
decoder := schema.NewDecoder()
|
|
err := decoder.Decode(incomingWebhookPayload, r.PostForm)
|
|
|
|
if err != nil {
|
|
c.Err = model.NewAppError("incomingWebhook",
|
|
"api.webhook.incoming.error",
|
|
nil,
|
|
"webhook_id="+id+", error: "+err.Error(),
|
|
http.StatusBadRequest,
|
|
)
|
|
return
|
|
}
|
|
} else {
|
|
incomingWebhookPayload, err = decodePayload(r.Body)
|
|
if err != nil {
|
|
c.Err = err
|
|
return
|
|
}
|
|
}
|
|
|
|
err = c.App.HandleIncomingWebhook(id, incomingWebhookPayload)
|
|
if err != nil {
|
|
c.Err = err
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
w.Write([]byte("ok"))
|
|
}
|
|
|
|
func commandWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
params := mux.Vars(r)
|
|
id := params["id"]
|
|
|
|
response, err := model.CommandResponseFromHTTPBody(r.Header.Get("Content-Type"), r.Body)
|
|
if err != nil {
|
|
c.Err = model.NewAppError("commandWebhook", "web.command_webhook.parse.app_error", nil, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
appErr := c.App.HandleCommandWebhook(id, response)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
w.Write([]byte("ok"))
|
|
}
|
|
|
|
func decodePayload(payload io.Reader) (*model.IncomingWebhookRequest, *model.AppError) {
|
|
incomingWebhookPayload, decodeError := model.IncomingWebhookRequestFromJson(payload)
|
|
|
|
if decodeError != nil {
|
|
return nil, decodeError
|
|
}
|
|
|
|
return incomingWebhookPayload, nil
|
|
}
|