MM-10201: querystring for get slash commands (#8779)

* pass GET slash command payloads through query string

Previously, both GET and POST requests received the payload via the
body, but this was incorrect for GET requests. Now, the payloads for GET
requests is sent via the query string.

* reorder tests for clarity

* switch command tests to use httptest servers

* restore original test command endpoints
This commit is contained in:
Jesse Hallam
2018-05-14 13:24:22 -04:00
committed by Joram Wilander
parent 6a9aa855d1
commit a1656dffa9
2 changed files with 137 additions and 62 deletions

View File

@@ -5,9 +5,13 @@ package api4
import (
"fmt"
"strings"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/model"
)
@@ -392,7 +396,7 @@ func TestRegenToken(t *testing.T) {
}
}
func TestExecuteCommand(t *testing.T) {
func TestExecuteInvalidCommand(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer th.TearDown()
Client := th.Client
@@ -407,56 +411,19 @@ func TestExecuteCommand(t *testing.T) {
})
}()
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost" })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "127.0.0.0/8" })
postCmd := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port) + model.API_URL_SUFFIX_V4 + "/teams/command_test",
Method: model.COMMAND_METHOD_POST,
Trigger: "postcommand",
}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rc := &model.CommandResponse{}
if _, err := th.App.CreateCommand(postCmd); err != nil {
t.Fatal("failed to create post command")
}
commandResponse, resp := Client.ExecuteCommand(channel.Id, "/postcommand")
CheckNoError(t, resp)
if commandResponse == nil {
t.Fatal("command response should have returned")
}
posts, err := th.App.GetPostsPage(channel.Id, 0, 10)
if err != nil || posts == nil || len(posts.Order) != 3 {
t.Fatal("Test command failed to send")
}
cmdPosted := false
for _, post := range posts.Posts {
if strings.Contains(post.Message, "test command response") {
if post.Type != "custom_test" {
t.Fatal("wrong type set in slash command post")
}
if post.Props["someprop"] != "somevalue" {
t.Fatal("wrong prop set in slash command post")
}
cmdPosted = true
break
}
}
if !cmdPosted {
t.Fatal("Test command response failed to post")
}
w.Write([]byte(rc.ToJson()))
}))
defer ts.Close()
getCmd := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port) + model.API_URL_SUFFIX_V4 + "/teams/command_test",
URL: fmt.Sprintf("%s/%s/teams/command_test", ts.URL, model.API_URL_SUFFIX_V4),
Method: model.COMMAND_METHOD_GET,
Trigger: "getcommand",
}
@@ -465,19 +432,7 @@ func TestExecuteCommand(t *testing.T) {
t.Fatal("failed to create get command")
}
commandResponse, resp = Client.ExecuteCommand(channel.Id, "/getcommand")
CheckNoError(t, resp)
if commandResponse == nil {
t.Fatal("command response should have returned")
}
posts, err = th.App.GetPostsPage(channel.Id, 0, 10)
if err != nil || posts == nil || len(posts.Order) != 4 {
t.Fatal("Test command failed to send")
}
_, resp = Client.ExecuteCommand(channel.Id, "")
_, resp := Client.ExecuteCommand(channel.Id, "")
CheckBadRequestStatus(t, resp)
_, resp = Client.ExecuteCommand(channel.Id, "/")
@@ -504,6 +459,124 @@ func TestExecuteCommand(t *testing.T) {
CheckNoError(t, resp)
}
func TestExecuteGetCommand(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer th.TearDown()
Client := th.Client
channel := th.BasicChannel
enableCommands := *th.App.Config().ServiceSettings.EnableCommands
allowedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections
defer func() {
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.ServiceSettings.AllowedUntrustedInternalConnections = &allowedInternalConnections
})
}()
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "127.0.0.0/8" })
token := model.NewId()
expectedCommandResponse := &model.CommandResponse{
Text: "test get command response",
ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
Type: "custom_test",
Props: map[string]interface{}{"someprop": "somevalue"},
}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodGet, r.Method)
values, err := url.ParseQuery(r.URL.RawQuery)
require.NoError(t, err)
require.Equal(t, token, values.Get("token"))
require.Equal(t, th.BasicTeam.Name, values.Get("team_domain"))
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(expectedCommandResponse.ToJson()))
}))
defer ts.Close()
getCmd := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: fmt.Sprintf("%s/%s/teams/command_test", ts.URL, model.API_URL_SUFFIX_V4),
Method: model.COMMAND_METHOD_GET,
Trigger: "getcommand",
Token: token,
}
if _, err := th.App.CreateCommand(getCmd); err != nil {
t.Fatal("failed to create get command")
}
commandResponse, resp := Client.ExecuteCommand(channel.Id, "/getcommand")
CheckNoError(t, resp)
expectedCommandResponse.Props["from_webhook"] = "true"
require.Equal(t, expectedCommandResponse, commandResponse)
}
func TestExecutePostCommand(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer th.TearDown()
Client := th.Client
channel := th.BasicChannel
enableCommands := *th.App.Config().ServiceSettings.EnableCommands
allowedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections
defer func() {
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.ServiceSettings.AllowedUntrustedInternalConnections = &allowedInternalConnections
})
}()
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "127.0.0.0/8" })
token := model.NewId()
expectedCommandResponse := &model.CommandResponse{
Text: "test post command response",
ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
Type: "custom_test",
Props: map[string]interface{}{"someprop": "somevalue"},
}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
r.ParseForm()
require.Equal(t, token, r.FormValue("token"))
require.Equal(t, th.BasicTeam.Name, r.FormValue("team_domain"))
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(expectedCommandResponse.ToJson()))
}))
defer ts.Close()
getCmd := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: fmt.Sprintf("%s/%s/teams/command_test", ts.URL, model.API_URL_SUFFIX_V4),
Method: model.COMMAND_METHOD_POST,
Trigger: "postcommand",
Token: token,
}
if _, err := th.App.CreateCommand(getCmd); err != nil {
t.Fatal("failed to create get command")
}
commandResponse, resp := Client.ExecuteCommand(channel.Id, "/postcommand")
CheckNoError(t, resp)
expectedCommandResponse.Props["from_webhook"] = "true"
require.Equal(t, expectedCommandResponse, commandResponse)
}
func TestExecuteCommandAgainstChannelOnAnotherTeam(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()

View File

@@ -230,12 +230,14 @@ func (a *App) ExecuteCommand(args *model.CommandArgs) (*model.CommandResponse, *
p.Set("response_url", args.SiteURL+"/hooks/commands/"+hook.Id)
}
method := "POST"
var req *http.Request
if cmd.Method == model.COMMAND_METHOD_GET {
method = "GET"
req, _ = http.NewRequest(http.MethodGet, cmd.URL, nil)
req.URL.RawQuery = p.Encode()
} else {
req, _ = http.NewRequest(http.MethodPost, cmd.URL, strings.NewReader(p.Encode()))
}
req, _ := http.NewRequest(method, cmd.URL, strings.NewReader(p.Encode()))
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Token "+cmd.Token)
if cmd.Method == model.COMMAND_METHOD_POST {