github.com/mad-app/mattermost-server@v5.11.1+incompatible/api4/command_test.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package api4
     5  
     6  import (
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"net/url"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/mattermost/mattermost-server/model"
    16  )
    17  
    18  func TestCreateCommand(t *testing.T) {
    19  	th := Setup().InitBasic()
    20  	defer th.TearDown()
    21  	Client := th.Client
    22  
    23  	enableCommands := *th.App.Config().ServiceSettings.EnableCommands
    24  	defer func() {
    25  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
    26  	}()
    27  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
    28  
    29  	newCmd := &model.Command{
    30  		CreatorId: th.BasicUser.Id,
    31  		TeamId:    th.BasicTeam.Id,
    32  		URL:       "http://nowhere.com",
    33  		Method:    model.COMMAND_METHOD_POST,
    34  		Trigger:   "trigger"}
    35  
    36  	_, resp := Client.CreateCommand(newCmd)
    37  	CheckForbiddenStatus(t, resp)
    38  
    39  	createdCmd, resp := th.SystemAdminClient.CreateCommand(newCmd)
    40  	CheckNoError(t, resp)
    41  	CheckCreatedStatus(t, resp)
    42  	if createdCmd.CreatorId != th.SystemAdminUser.Id {
    43  		t.Fatal("user ids didn't match")
    44  	}
    45  	if createdCmd.TeamId != th.BasicTeam.Id {
    46  		t.Fatal("team ids didn't match")
    47  	}
    48  
    49  	_, resp = th.SystemAdminClient.CreateCommand(newCmd)
    50  	CheckBadRequestStatus(t, resp)
    51  	CheckErrorMessage(t, resp, "api.command.duplicate_trigger.app_error")
    52  
    53  	newCmd.Method = "Wrong"
    54  	newCmd.Trigger = "testcommand"
    55  	_, resp = th.SystemAdminClient.CreateCommand(newCmd)
    56  	CheckBadRequestStatus(t, resp)
    57  	CheckErrorMessage(t, resp, "model.command.is_valid.method.app_error")
    58  
    59  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = false })
    60  	newCmd.Method = "P"
    61  	newCmd.Trigger = "testcommand"
    62  	_, resp = th.SystemAdminClient.CreateCommand(newCmd)
    63  	CheckNotImplementedStatus(t, resp)
    64  	CheckErrorMessage(t, resp, "api.command.disabled.app_error")
    65  }
    66  
    67  func TestUpdateCommand(t *testing.T) {
    68  	th := Setup().InitBasic()
    69  	defer th.TearDown()
    70  	Client := th.SystemAdminClient
    71  	user := th.SystemAdminUser
    72  	team := th.BasicTeam
    73  
    74  	enableCommands := *th.App.Config().ServiceSettings.EnableCommands
    75  	defer func() {
    76  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
    77  	}()
    78  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
    79  
    80  	cmd1 := &model.Command{
    81  		CreatorId: user.Id,
    82  		TeamId:    team.Id,
    83  		URL:       "http://nowhere.com",
    84  		Method:    model.COMMAND_METHOD_POST,
    85  		Trigger:   "trigger1",
    86  	}
    87  
    88  	cmd1, _ = th.App.CreateCommand(cmd1)
    89  
    90  	cmd2 := &model.Command{
    91  		CreatorId: GenerateTestId(),
    92  		TeamId:    team.Id,
    93  		URL:       "http://nowhere.com/change",
    94  		Method:    model.COMMAND_METHOD_GET,
    95  		Trigger:   "trigger2",
    96  		Id:        cmd1.Id,
    97  		Token:     "tokenchange",
    98  	}
    99  
   100  	rcmd, resp := Client.UpdateCommand(cmd2)
   101  	CheckNoError(t, resp)
   102  
   103  	if rcmd.Trigger != cmd2.Trigger {
   104  		t.Fatal("Trigger should have updated")
   105  	}
   106  
   107  	if rcmd.Method != cmd2.Method {
   108  		t.Fatal("Method should have updated")
   109  	}
   110  
   111  	if rcmd.URL != cmd2.URL {
   112  		t.Fatal("URL should have updated")
   113  	}
   114  
   115  	if rcmd.CreatorId != cmd1.CreatorId {
   116  		t.Fatal("CreatorId should have not updated")
   117  	}
   118  
   119  	if rcmd.Token != cmd1.Token {
   120  		t.Fatal("Token should have not updated")
   121  	}
   122  
   123  	cmd2.Id = GenerateTestId()
   124  
   125  	rcmd, resp = Client.UpdateCommand(cmd2)
   126  	CheckNotFoundStatus(t, resp)
   127  
   128  	if rcmd != nil {
   129  		t.Fatal("should be empty")
   130  	}
   131  
   132  	cmd2.Id = "junk"
   133  
   134  	_, resp = Client.UpdateCommand(cmd2)
   135  	CheckBadRequestStatus(t, resp)
   136  
   137  	cmd2.Id = cmd1.Id
   138  	cmd2.TeamId = GenerateTestId()
   139  
   140  	_, resp = Client.UpdateCommand(cmd2)
   141  	CheckBadRequestStatus(t, resp)
   142  
   143  	cmd2.TeamId = team.Id
   144  
   145  	_, resp = th.Client.UpdateCommand(cmd2)
   146  	CheckForbiddenStatus(t, resp)
   147  
   148  	Client.Logout()
   149  	_, resp = Client.UpdateCommand(cmd2)
   150  	CheckUnauthorizedStatus(t, resp)
   151  }
   152  
   153  func TestDeleteCommand(t *testing.T) {
   154  	th := Setup().InitBasic()
   155  	defer th.TearDown()
   156  	Client := th.SystemAdminClient
   157  	user := th.SystemAdminUser
   158  	team := th.BasicTeam
   159  
   160  	enableCommands := *th.App.Config().ServiceSettings.EnableCommands
   161  	defer func() {
   162  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
   163  	}()
   164  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
   165  
   166  	cmd1 := &model.Command{
   167  		CreatorId: user.Id,
   168  		TeamId:    team.Id,
   169  		URL:       "http://nowhere.com",
   170  		Method:    model.COMMAND_METHOD_POST,
   171  		Trigger:   "trigger1",
   172  	}
   173  
   174  	rcmd1, _ := th.App.CreateCommand(cmd1)
   175  
   176  	ok, resp := Client.DeleteCommand(rcmd1.Id)
   177  	CheckNoError(t, resp)
   178  
   179  	if !ok {
   180  		t.Fatal("should have returned true")
   181  	}
   182  
   183  	rcmd1, _ = th.App.GetCommand(rcmd1.Id)
   184  	if rcmd1 != nil {
   185  		t.Fatal("should be nil")
   186  	}
   187  
   188  	ok, resp = Client.DeleteCommand("junk")
   189  	CheckBadRequestStatus(t, resp)
   190  
   191  	if ok {
   192  		t.Fatal("should have returned false")
   193  	}
   194  
   195  	_, resp = Client.DeleteCommand(GenerateTestId())
   196  	CheckNotFoundStatus(t, resp)
   197  
   198  	cmd2 := &model.Command{
   199  		CreatorId: user.Id,
   200  		TeamId:    team.Id,
   201  		URL:       "http://nowhere.com",
   202  		Method:    model.COMMAND_METHOD_POST,
   203  		Trigger:   "trigger2",
   204  	}
   205  
   206  	rcmd2, _ := th.App.CreateCommand(cmd2)
   207  
   208  	_, resp = th.Client.DeleteCommand(rcmd2.Id)
   209  	CheckForbiddenStatus(t, resp)
   210  
   211  	Client.Logout()
   212  	_, resp = Client.DeleteCommand(rcmd2.Id)
   213  	CheckUnauthorizedStatus(t, resp)
   214  }
   215  
   216  func TestListCommands(t *testing.T) {
   217  	th := Setup().InitBasic()
   218  	defer th.TearDown()
   219  	Client := th.Client
   220  
   221  	enableCommands := *th.App.Config().ServiceSettings.EnableCommands
   222  	defer func() {
   223  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
   224  	}()
   225  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
   226  
   227  	newCmd := &model.Command{
   228  		CreatorId: th.BasicUser.Id,
   229  		TeamId:    th.BasicTeam.Id,
   230  		URL:       "http://nowhere.com",
   231  		Method:    model.COMMAND_METHOD_POST,
   232  		Trigger:   "custom_command"}
   233  
   234  	_, resp := th.SystemAdminClient.CreateCommand(newCmd)
   235  	CheckNoError(t, resp)
   236  
   237  	t.Run("ListSystemAndCustomCommands", func(t *testing.T) {
   238  		listCommands, resp := th.SystemAdminClient.ListCommands(th.BasicTeam.Id, false)
   239  		CheckNoError(t, resp)
   240  
   241  		foundEcho := false
   242  		foundCustom := false
   243  		for _, command := range listCommands {
   244  			if command.Trigger == "echo" {
   245  				foundEcho = true
   246  			}
   247  			if command.Trigger == "custom_command" {
   248  				foundCustom = true
   249  			}
   250  		}
   251  		if !foundEcho {
   252  			t.Fatal("Couldn't find echo command")
   253  		}
   254  		if !foundCustom {
   255  			t.Fatal("Should list the custom command")
   256  		}
   257  	})
   258  
   259  	t.Run("ListCustomOnlyCommands", func(t *testing.T) {
   260  		listCommands, resp := th.SystemAdminClient.ListCommands(th.BasicTeam.Id, true)
   261  		CheckNoError(t, resp)
   262  
   263  		if len(listCommands) > 1 {
   264  			t.Fatal("Should list just one custom command")
   265  		}
   266  		if listCommands[0].Trigger != "custom_command" {
   267  			t.Fatal("Wrong custom command trigger")
   268  		}
   269  	})
   270  
   271  	t.Run("UserWithNoPermissionForCustomCommands", func(t *testing.T) {
   272  		_, resp := Client.ListCommands(th.BasicTeam.Id, true)
   273  		CheckForbiddenStatus(t, resp)
   274  	})
   275  
   276  	t.Run("RegularUserCanListOnlySystemCommands", func(t *testing.T) {
   277  		listCommands, resp := Client.ListCommands(th.BasicTeam.Id, false)
   278  		CheckNoError(t, resp)
   279  
   280  		foundEcho := false
   281  		foundCustom := false
   282  		for _, command := range listCommands {
   283  			if command.Trigger == "echo" {
   284  				foundEcho = true
   285  			}
   286  			if command.Trigger == "custom_command" {
   287  				foundCustom = true
   288  			}
   289  		}
   290  		if !foundEcho {
   291  			t.Fatal("Couldn't find echo command")
   292  		}
   293  		if foundCustom {
   294  			t.Fatal("Should not list the custom command")
   295  		}
   296  	})
   297  }
   298  
   299  func TestListAutocompleteCommands(t *testing.T) {
   300  	th := Setup().InitBasic()
   301  	defer th.TearDown()
   302  	Client := th.Client
   303  
   304  	newCmd := &model.Command{
   305  		CreatorId: th.BasicUser.Id,
   306  		TeamId:    th.BasicTeam.Id,
   307  		URL:       "http://nowhere.com",
   308  		Method:    model.COMMAND_METHOD_POST,
   309  		Trigger:   "custom_command"}
   310  
   311  	_, resp := th.SystemAdminClient.CreateCommand(newCmd)
   312  	CheckNoError(t, resp)
   313  
   314  	t.Run("ListAutocompleteCommandsOnly", func(t *testing.T) {
   315  		listCommands, resp := th.SystemAdminClient.ListAutocompleteCommands(th.BasicTeam.Id)
   316  		CheckNoError(t, resp)
   317  
   318  		foundEcho := false
   319  		foundCustom := false
   320  		for _, command := range listCommands {
   321  			if command.Trigger == "echo" {
   322  				foundEcho = true
   323  			}
   324  			if command.Trigger == "custom_command" {
   325  				foundCustom = true
   326  			}
   327  		}
   328  		if !foundEcho {
   329  			t.Fatal("Couldn't find echo command")
   330  		}
   331  		if foundCustom {
   332  			t.Fatal("Should not list the custom command")
   333  		}
   334  	})
   335  
   336  	t.Run("RegularUserCanListOnlySystemCommands", func(t *testing.T) {
   337  		listCommands, resp := Client.ListAutocompleteCommands(th.BasicTeam.Id)
   338  		CheckNoError(t, resp)
   339  
   340  		foundEcho := false
   341  		foundCustom := false
   342  		for _, command := range listCommands {
   343  			if command.Trigger == "echo" {
   344  				foundEcho = true
   345  			}
   346  			if command.Trigger == "custom_command" {
   347  				foundCustom = true
   348  			}
   349  		}
   350  		if !foundEcho {
   351  			t.Fatal("Couldn't find echo command")
   352  		}
   353  		if foundCustom {
   354  			t.Fatal("Should not list the custom command")
   355  		}
   356  	})
   357  }
   358  
   359  func TestRegenToken(t *testing.T) {
   360  	th := Setup().InitBasic()
   361  	defer th.TearDown()
   362  	Client := th.Client
   363  
   364  	enableCommands := *th.App.Config().ServiceSettings.EnableCommands
   365  	defer func() {
   366  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
   367  	}()
   368  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
   369  
   370  	newCmd := &model.Command{
   371  		CreatorId: th.BasicUser.Id,
   372  		TeamId:    th.BasicTeam.Id,
   373  		URL:       "http://nowhere.com",
   374  		Method:    model.COMMAND_METHOD_POST,
   375  		Trigger:   "trigger"}
   376  
   377  	createdCmd, resp := th.SystemAdminClient.CreateCommand(newCmd)
   378  	CheckNoError(t, resp)
   379  	CheckCreatedStatus(t, resp)
   380  
   381  	token, resp := th.SystemAdminClient.RegenCommandToken(createdCmd.Id)
   382  	CheckNoError(t, resp)
   383  	if token == createdCmd.Token {
   384  		t.Fatal("should update the token")
   385  	}
   386  
   387  	token, resp = Client.RegenCommandToken(createdCmd.Id)
   388  	CheckForbiddenStatus(t, resp)
   389  	if token != "" {
   390  		t.Fatal("should not return the token")
   391  	}
   392  }
   393  
   394  func TestExecuteInvalidCommand(t *testing.T) {
   395  	th := Setup().InitBasic()
   396  	defer th.TearDown()
   397  	Client := th.Client
   398  	channel := th.BasicChannel
   399  
   400  	enableCommands := *th.App.Config().ServiceSettings.EnableCommands
   401  	allowedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections
   402  	defer func() {
   403  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
   404  		th.App.UpdateConfig(func(cfg *model.Config) {
   405  			cfg.ServiceSettings.AllowedUntrustedInternalConnections = &allowedInternalConnections
   406  		})
   407  	}()
   408  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
   409  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "127.0.0.0/8" })
   410  
   411  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   412  		rc := &model.CommandResponse{}
   413  
   414  		w.Write([]byte(rc.ToJson()))
   415  	}))
   416  	defer ts.Close()
   417  
   418  	getCmd := &model.Command{
   419  		CreatorId: th.BasicUser.Id,
   420  		TeamId:    th.BasicTeam.Id,
   421  		URL:       ts.URL,
   422  		Method:    model.COMMAND_METHOD_GET,
   423  		Trigger:   "getcommand",
   424  	}
   425  
   426  	if _, err := th.App.CreateCommand(getCmd); err != nil {
   427  		t.Fatal("failed to create get command")
   428  	}
   429  
   430  	_, resp := Client.ExecuteCommand(channel.Id, "")
   431  	CheckBadRequestStatus(t, resp)
   432  
   433  	_, resp = Client.ExecuteCommand(channel.Id, "/")
   434  	CheckBadRequestStatus(t, resp)
   435  
   436  	_, resp = Client.ExecuteCommand(channel.Id, "getcommand")
   437  	CheckBadRequestStatus(t, resp)
   438  
   439  	_, resp = Client.ExecuteCommand(channel.Id, "/junk")
   440  	CheckNotFoundStatus(t, resp)
   441  
   442  	otherUser := th.CreateUser()
   443  	Client.Login(otherUser.Email, otherUser.Password)
   444  
   445  	_, resp = Client.ExecuteCommand(channel.Id, "/getcommand")
   446  	CheckForbiddenStatus(t, resp)
   447  
   448  	Client.Logout()
   449  
   450  	_, resp = Client.ExecuteCommand(channel.Id, "/getcommand")
   451  	CheckUnauthorizedStatus(t, resp)
   452  
   453  	_, resp = th.SystemAdminClient.ExecuteCommand(channel.Id, "/getcommand")
   454  	CheckNoError(t, resp)
   455  }
   456  
   457  func TestExecuteGetCommand(t *testing.T) {
   458  	th := Setup().InitBasic()
   459  	defer th.TearDown()
   460  	Client := th.Client
   461  	channel := th.BasicChannel
   462  
   463  	enableCommands := *th.App.Config().ServiceSettings.EnableCommands
   464  	allowedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections
   465  	defer func() {
   466  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
   467  		th.App.UpdateConfig(func(cfg *model.Config) {
   468  			cfg.ServiceSettings.AllowedUntrustedInternalConnections = &allowedInternalConnections
   469  		})
   470  	}()
   471  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
   472  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "127.0.0.0/8" })
   473  
   474  	token := model.NewId()
   475  	expectedCommandResponse := &model.CommandResponse{
   476  		Text:         "test get command response",
   477  		ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
   478  		Type:         "custom_test",
   479  		Props:        map[string]interface{}{"someprop": "somevalue"},
   480  	}
   481  
   482  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   483  		require.Equal(t, http.MethodGet, r.Method)
   484  
   485  		values, err := url.ParseQuery(r.URL.RawQuery)
   486  		require.NoError(t, err)
   487  
   488  		require.Equal(t, token, values.Get("token"))
   489  		require.Equal(t, th.BasicTeam.Name, values.Get("team_domain"))
   490  		require.Equal(t, "ourCommand", values.Get("cmd"))
   491  
   492  		w.Header().Set("Content-Type", "application/json")
   493  		w.Write([]byte(expectedCommandResponse.ToJson()))
   494  	}))
   495  	defer ts.Close()
   496  
   497  	getCmd := &model.Command{
   498  		CreatorId: th.BasicUser.Id,
   499  		TeamId:    th.BasicTeam.Id,
   500  		URL:       ts.URL + "/?cmd=ourCommand",
   501  		Method:    model.COMMAND_METHOD_GET,
   502  		Trigger:   "getcommand",
   503  		Token:     token,
   504  	}
   505  
   506  	if _, err := th.App.CreateCommand(getCmd); err != nil {
   507  		t.Fatal("failed to create get command")
   508  	}
   509  
   510  	commandResponse, resp := Client.ExecuteCommand(channel.Id, "/getcommand")
   511  	CheckNoError(t, resp)
   512  	assert.True(t, len(commandResponse.TriggerId) == 26)
   513  
   514  	expectedCommandResponse.TriggerId = commandResponse.TriggerId
   515  	expectedCommandResponse.Props["from_webhook"] = "true"
   516  	require.Equal(t, expectedCommandResponse, commandResponse)
   517  }
   518  
   519  func TestExecutePostCommand(t *testing.T) {
   520  	th := Setup().InitBasic()
   521  	defer th.TearDown()
   522  	Client := th.Client
   523  	channel := th.BasicChannel
   524  
   525  	enableCommands := *th.App.Config().ServiceSettings.EnableCommands
   526  	allowedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections
   527  	defer func() {
   528  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
   529  		th.App.UpdateConfig(func(cfg *model.Config) {
   530  			cfg.ServiceSettings.AllowedUntrustedInternalConnections = &allowedInternalConnections
   531  		})
   532  	}()
   533  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
   534  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "127.0.0.0/8" })
   535  
   536  	token := model.NewId()
   537  	expectedCommandResponse := &model.CommandResponse{
   538  		Text:         "test post command response",
   539  		ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
   540  		Type:         "custom_test",
   541  		Props:        map[string]interface{}{"someprop": "somevalue"},
   542  	}
   543  
   544  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   545  		require.Equal(t, http.MethodPost, r.Method)
   546  
   547  		r.ParseForm()
   548  
   549  		require.Equal(t, token, r.FormValue("token"))
   550  		require.Equal(t, th.BasicTeam.Name, r.FormValue("team_domain"))
   551  
   552  		w.Header().Set("Content-Type", "application/json")
   553  		w.Write([]byte(expectedCommandResponse.ToJson()))
   554  	}))
   555  	defer ts.Close()
   556  
   557  	postCmd := &model.Command{
   558  		CreatorId: th.BasicUser.Id,
   559  		TeamId:    th.BasicTeam.Id,
   560  		URL:       ts.URL,
   561  		Method:    model.COMMAND_METHOD_POST,
   562  		Trigger:   "postcommand",
   563  		Token:     token,
   564  	}
   565  
   566  	if _, err := th.App.CreateCommand(postCmd); err != nil {
   567  		t.Fatal("failed to create get command")
   568  	}
   569  
   570  	commandResponse, resp := Client.ExecuteCommand(channel.Id, "/postcommand")
   571  	CheckNoError(t, resp)
   572  	assert.True(t, len(commandResponse.TriggerId) == 26)
   573  
   574  	expectedCommandResponse.TriggerId = commandResponse.TriggerId
   575  	expectedCommandResponse.Props["from_webhook"] = "true"
   576  	require.Equal(t, expectedCommandResponse, commandResponse)
   577  
   578  }
   579  
   580  func TestExecuteCommandAgainstChannelOnAnotherTeam(t *testing.T) {
   581  	th := Setup().InitBasic()
   582  	defer th.TearDown()
   583  	Client := th.Client
   584  	channel := th.BasicChannel
   585  
   586  	enableCommands := *th.App.Config().ServiceSettings.EnableCommands
   587  	allowedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections
   588  	defer func() {
   589  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
   590  		th.App.UpdateConfig(func(cfg *model.Config) {
   591  			cfg.ServiceSettings.AllowedUntrustedInternalConnections = &allowedInternalConnections
   592  		})
   593  	}()
   594  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
   595  	th.App.UpdateConfig(func(cfg *model.Config) {
   596  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
   597  	})
   598  
   599  	expectedCommandResponse := &model.CommandResponse{
   600  		Text:         "test post command response",
   601  		ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
   602  		Type:         "custom_test",
   603  		Props:        map[string]interface{}{"someprop": "somevalue"},
   604  	}
   605  
   606  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   607  		w.Header().Set("Content-Type", "application/json")
   608  		w.Write([]byte(expectedCommandResponse.ToJson()))
   609  	}))
   610  	defer ts.Close()
   611  
   612  	// create a slash command on some other team where we have permission to do so
   613  	team2 := th.CreateTeam()
   614  	postCmd := &model.Command{
   615  		CreatorId: th.BasicUser.Id,
   616  		TeamId:    team2.Id,
   617  		URL:       ts.URL,
   618  		Method:    model.COMMAND_METHOD_POST,
   619  		Trigger:   "postcommand",
   620  	}
   621  	if _, err := th.App.CreateCommand(postCmd); err != nil {
   622  		t.Fatal("failed to create post command")
   623  	}
   624  
   625  	// the execute command endpoint will always search for the command by trigger and team id, inferring team id from the
   626  	// channel id, so there is no way to use that slash command on a channel that belongs to some other team
   627  	_, resp := Client.ExecuteCommand(channel.Id, "/postcommand")
   628  	CheckNotFoundStatus(t, resp)
   629  }
   630  
   631  func TestExecuteCommandAgainstChannelUserIsNotIn(t *testing.T) {
   632  	th := Setup().InitBasic()
   633  	defer th.TearDown()
   634  	client := th.Client
   635  
   636  	enableCommands := *th.App.Config().ServiceSettings.EnableCommands
   637  	allowedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections
   638  	defer func() {
   639  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
   640  		th.App.UpdateConfig(func(cfg *model.Config) {
   641  			cfg.ServiceSettings.AllowedUntrustedInternalConnections = &allowedInternalConnections
   642  		})
   643  	}()
   644  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
   645  	th.App.UpdateConfig(func(cfg *model.Config) {
   646  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
   647  	})
   648  
   649  	expectedCommandResponse := &model.CommandResponse{
   650  		Text:         "test post command response",
   651  		ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
   652  		Type:         "custom_test",
   653  		Props:        map[string]interface{}{"someprop": "somevalue"},
   654  	}
   655  
   656  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   657  		w.Header().Set("Content-Type", "application/json")
   658  		w.Write([]byte(expectedCommandResponse.ToJson()))
   659  	}))
   660  	defer ts.Close()
   661  
   662  	// create a slash command on some other team where we have permission to do so
   663  	team2 := th.CreateTeam()
   664  	postCmd := &model.Command{
   665  		CreatorId: th.BasicUser.Id,
   666  		TeamId:    team2.Id,
   667  		URL:       ts.URL,
   668  		Method:    model.COMMAND_METHOD_POST,
   669  		Trigger:   "postcommand",
   670  	}
   671  	if _, err := th.App.CreateCommand(postCmd); err != nil {
   672  		t.Fatal("failed to create post command")
   673  	}
   674  
   675  	// make a channel on that team, ensuring that our test user isn't in it
   676  	channel2 := th.CreateChannelWithClientAndTeam(client, model.CHANNEL_OPEN, team2.Id)
   677  	if success, _ := client.RemoveUserFromChannel(channel2.Id, th.BasicUser.Id); !success {
   678  		t.Fatal("Failed to remove user from channel")
   679  	}
   680  
   681  	// we should not be able to run the slash command in channel2, because we aren't in it
   682  	_, resp := client.ExecuteCommandWithTeam(channel2.Id, team2.Id, "/postcommand")
   683  	CheckForbiddenStatus(t, resp)
   684  }
   685  
   686  func TestExecuteCommandInDirectMessageChannel(t *testing.T) {
   687  	th := Setup().InitBasic()
   688  	defer th.TearDown()
   689  	client := th.Client
   690  
   691  	enableCommands := *th.App.Config().ServiceSettings.EnableCommands
   692  	allowedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections
   693  	defer func() {
   694  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
   695  		th.App.UpdateConfig(func(cfg *model.Config) {
   696  			cfg.ServiceSettings.AllowedUntrustedInternalConnections = &allowedInternalConnections
   697  		})
   698  	}()
   699  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
   700  	th.App.UpdateConfig(func(cfg *model.Config) {
   701  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
   702  	})
   703  
   704  	// create a team that the user isn't a part of
   705  	team2 := th.CreateTeam()
   706  
   707  	expectedCommandResponse := &model.CommandResponse{
   708  		Text:         "test post command response",
   709  		ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
   710  		Type:         "custom_test",
   711  		Props:        map[string]interface{}{"someprop": "somevalue"},
   712  	}
   713  
   714  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   715  		require.Equal(t, http.MethodPost, r.Method)
   716  		w.Header().Set("Content-Type", "application/json")
   717  		w.Write([]byte(expectedCommandResponse.ToJson()))
   718  	}))
   719  	defer ts.Close()
   720  
   721  	// create a slash command on some other team where we have permission to do so
   722  	postCmd := &model.Command{
   723  		CreatorId: th.BasicUser.Id,
   724  		TeamId:    team2.Id,
   725  		URL:       ts.URL,
   726  		Method:    model.COMMAND_METHOD_POST,
   727  		Trigger:   "postcommand",
   728  	}
   729  	if _, err := th.App.CreateCommand(postCmd); err != nil {
   730  		t.Fatal("failed to create post command")
   731  	}
   732  
   733  	// make a direct message channel
   734  	dmChannel, response := client.CreateDirectChannel(th.BasicUser.Id, th.BasicUser2.Id)
   735  	CheckCreatedStatus(t, response)
   736  
   737  	// we should be able to run the slash command in the DM channel
   738  	_, resp := client.ExecuteCommandWithTeam(dmChannel.Id, team2.Id, "/postcommand")
   739  	CheckOKStatus(t, resp)
   740  
   741  	// but we can't run the slash command in the DM channel if we sub in some other team's id
   742  	_, resp = client.ExecuteCommandWithTeam(dmChannel.Id, th.BasicTeam.Id, "/postcommand")
   743  	CheckNotFoundStatus(t, resp)
   744  }
   745  
   746  func TestExecuteCommandInTeamUserIsNotOn(t *testing.T) {
   747  	th := Setup().InitBasic()
   748  	defer th.TearDown()
   749  	client := th.Client
   750  
   751  	enableCommands := *th.App.Config().ServiceSettings.EnableCommands
   752  	allowedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections
   753  	defer func() {
   754  		th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
   755  		th.App.UpdateConfig(func(cfg *model.Config) {
   756  			cfg.ServiceSettings.AllowedUntrustedInternalConnections = &allowedInternalConnections
   757  		})
   758  	}()
   759  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
   760  	th.App.UpdateConfig(func(cfg *model.Config) {
   761  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
   762  	})
   763  
   764  	// create a team that the user isn't a part of
   765  	team2 := th.CreateTeam()
   766  
   767  	expectedCommandResponse := &model.CommandResponse{
   768  		Text:         "test post command response",
   769  		ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
   770  		Type:         "custom_test",
   771  		Props:        map[string]interface{}{"someprop": "somevalue"},
   772  	}
   773  
   774  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   775  		require.Equal(t, http.MethodPost, r.Method)
   776  		r.ParseForm()
   777  		require.Equal(t, team2.Name, r.FormValue("team_domain"))
   778  
   779  		w.Header().Set("Content-Type", "application/json")
   780  		w.Write([]byte(expectedCommandResponse.ToJson()))
   781  	}))
   782  	defer ts.Close()
   783  
   784  	// create a slash command on that team
   785  	postCmd := &model.Command{
   786  		CreatorId: th.BasicUser.Id,
   787  		TeamId:    team2.Id,
   788  		URL:       ts.URL,
   789  		Method:    model.COMMAND_METHOD_POST,
   790  		Trigger:   "postcommand",
   791  	}
   792  	if _, err := th.App.CreateCommand(postCmd); err != nil {
   793  		t.Fatal("failed to create post command")
   794  	}
   795  
   796  	// make a direct message channel
   797  	dmChannel, response := client.CreateDirectChannel(th.BasicUser.Id, th.BasicUser2.Id)
   798  	CheckCreatedStatus(t, response)
   799  
   800  	// we should be able to run the slash command in the DM channel
   801  	_, resp := client.ExecuteCommandWithTeam(dmChannel.Id, team2.Id, "/postcommand")
   802  	CheckOKStatus(t, resp)
   803  
   804  	// if the user is removed from the team, they should NOT be able to run the slash command in the DM channel
   805  	if success, _ := client.RemoveTeamMember(team2.Id, th.BasicUser.Id); !success {
   806  		t.Fatal("Failed to remove user from team")
   807  	}
   808  	_, resp = client.ExecuteCommandWithTeam(dmChannel.Id, team2.Id, "/postcommand")
   809  	CheckForbiddenStatus(t, resp)
   810  
   811  	// if we omit the team id from the request, the slash command will fail because this is a DM channel, and the
   812  	// team id can't be inherited from the channel
   813  	_, resp = client.ExecuteCommand(dmChannel.Id, "/postcommand")
   814  	CheckForbiddenStatus(t, resp)
   815  }