[MM-25677] Content-Type is optional (#14705)

* 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
Этот коммит содержится в:
Mario de Frutos Dieguez
2020-06-01 21:07:02 +02:00
коммит произвёл GitHub
родитель 6643a7c610
Коммит beadeaf8b5
2 изменённых файлов: 45 добавлений и 25 удалений

Просмотреть файл

@@ -28,11 +28,22 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
r.ParseForm()
var err *model.AppError
var mediaType string
incomingWebhookPayload := &model.IncomingWebhookRequest{}
mediaType, _, mimeErr := mime.ParseMediaType(r.Header.Get("Content-Type"))
if mimeErr != nil && mimeErr != mime.ErrInvalidMediaParameter {
c.Err = model.NewAppError("incomingWebhook", "api.webhook.incoming.error", nil, mimeErr.Error(), http.StatusBadRequest)
return
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() {
@@ -58,7 +69,12 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
err := decoder.Decode(incomingWebhookPayload, r.PostForm)
if err != nil {
c.Err = model.NewAppError("incomingWebhook", "api.webhook.incoming.error", nil, err.Error(), http.StatusBadRequest)
c.Err = model.NewAppError("incomingWebhook",
"api.webhook.incoming.error",
nil,
"webhook_id="+id+", error: "+err.Error(),
http.StatusBadRequest,
)
return
}
} else {

Просмотреть файл

@@ -40,89 +40,93 @@ func TestIncomingWebhook(t *testing.T) {
payload := "payload={\"text\": \"test text\"}"
resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(payload))
require.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
payload = "payload={\"text\": \"\"}"
resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(payload))
require.Nil(t, err)
assert.True(t, resp.StatusCode != http.StatusOK, "should have errored - no text to post")
assert.NotEqual(t, http.StatusOK, resp.StatusCode, "should have errored - no text post")
payload = "payload={\"text\": \"test text\", \"channel\": \"junk\"}"
resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(payload))
require.Nil(t, err)
assert.True(t, resp.StatusCode != http.StatusOK, "should have errored - bad channel")
assert.NotEqual(t, http.StatusOK, resp.StatusCode, "should have errored - bad channel")
payload = "payload={\"text\": \"test text\"}"
resp, err = http.Post(ApiClient.Url+"/hooks/abc123", "application/x-www-form-urlencoded", strings.NewReader(payload))
require.Nil(t, err)
assert.True(t, resp.StatusCode != http.StatusOK, "should have errored - bad hook")
assert.NotEqual(t, http.StatusOK, resp.StatusCode, "should have errored - bad hook")
resp, err = http.Post(url, "application/json", strings.NewReader("{\"text\":\"this is a test\"}"))
require.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
text := `this is a \"test\"
that contains a newline and a tab`
resp, err = http.Post(url, "application/json", strings.NewReader("{\"text\":\""+text+"\"}"))
require.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = http.Post(url, "application/json", strings.NewReader(fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"%s\"}", th.BasicChannel.Name)))
require.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = http.Post(url, "application/json", strings.NewReader(fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"#%s\"}", th.BasicChannel.Name)))
require.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = http.Post(url, "application/json", strings.NewReader(fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"@%s\"}", th.BasicUser.Username)))
require.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader("payload={\"text\":\"this is a test\"}"))
require.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader("payload={\"text\":\""+text+"\"}"))
assert.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = http.Post(url, "AppLicaTion/x-www-Form-urlencoded", strings.NewReader("payload={\"text\":\""+text+"\"}"))
assert.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = http.Post(url, "application/x-www-form-urlencoded;charset=utf-8", strings.NewReader("payload={\"text\":\""+text+"\"}"))
assert.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = http.Post(url, "application/x-www-form-urlencoded; charset=utf-8", strings.NewReader("payload={\"text\":\""+text+"\"}"))
assert.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = http.Post(url, "application/x-www-form-urlencoded wrongtext", strings.NewReader("payload={\"text\":\""+text+"\"}"))
assert.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusBadRequest)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
resp, err = http.Post(url, "application/json", strings.NewReader("{\"text\":\""+tooLongText+"\"}"))
require.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = http.Post(url, "application/x-www-form-urlencoded", strings.NewReader("{\"text\":\""+tooLongText+"\"}"))
assert.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusBadRequest)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
resp, err = http.Post(url, "application/json", strings.NewReader("payload={\"text\":\""+text+"\"}"))
assert.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusBadRequest)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
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--"
resp, err = http.Post(ApiClient.Url+"/hooks/"+hook.Id, "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW", strings.NewReader(payloadMultiPart))
require.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusOK)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = http.Post(url, "mimetype/wrong", strings.NewReader("payload={\"text\":\""+text+"\"}"))
assert.Nil(t, err)
assert.True(t, resp.StatusCode == http.StatusBadRequest)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
resp, err = http.Post(url, "", strings.NewReader("{\"text\":\""+text+"\"}"))
assert.Nil(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("WebhookExperimentalReadOnly", func(t *testing.T) {