github.com/xzl8028/xenia-server@v0.0.0-20190809101854-18450a97da63/app/integration_action_test.go (about)

     1  // Copyright (c) 2017-present Xenia, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  
    17  	"github.com/xzl8028/xenia-server/model"
    18  )
    19  
    20  // Test for MM-13598 where an invalid integration URL was causing a crash
    21  func TestPostActionInvalidURL(t *testing.T) {
    22  	th := Setup(t).InitBasic()
    23  	defer th.TearDown()
    24  
    25  	th.App.UpdateConfig(func(cfg *model.Config) {
    26  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
    27  	})
    28  
    29  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    30  		request := model.PostActionIntegrationRequestFromJson(r.Body)
    31  		assert.NotNil(t, request)
    32  	}))
    33  	defer ts.Close()
    34  
    35  	interactivePost := model.Post{
    36  		Message:       "Interactive post",
    37  		ChannelId:     th.BasicChannel.Id,
    38  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
    39  		UserId:        th.BasicUser.Id,
    40  		Props: model.StringInterface{
    41  			"attachments": []*model.SlackAttachment{
    42  				{
    43  					Text: "hello",
    44  					Actions: []*model.PostAction{
    45  						{
    46  							Integration: &model.PostActionIntegration{
    47  								URL: ":test",
    48  							},
    49  							Name: "action",
    50  							Type: "some_type",
    51  						},
    52  					},
    53  				},
    54  			},
    55  		},
    56  	}
    57  
    58  	post, err := th.App.CreatePostAsUser(&interactivePost, "")
    59  	require.Nil(t, err)
    60  	attachments, ok := post.Props["attachments"].([]*model.SlackAttachment)
    61  	require.True(t, ok)
    62  	require.NotEmpty(t, attachments[0].Actions)
    63  	require.NotEmpty(t, attachments[0].Actions[0].Id)
    64  
    65  	_, err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "")
    66  	require.NotNil(t, err)
    67  }
    68  
    69  func TestPostAction(t *testing.T) {
    70  	th := Setup(t).InitBasic()
    71  	defer th.TearDown()
    72  
    73  	th.App.UpdateConfig(func(cfg *model.Config) {
    74  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
    75  	})
    76  
    77  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    78  		request := model.PostActionIntegrationRequestFromJson(r.Body)
    79  		assert.NotNil(t, request)
    80  
    81  		assert.Equal(t, request.UserId, th.BasicUser.Id)
    82  		assert.Equal(t, request.ChannelId, th.BasicChannel.Id)
    83  		assert.Equal(t, request.TeamId, th.BasicTeam.Id)
    84  		assert.True(t, len(request.TriggerId) > 0)
    85  		if request.Type == model.POST_ACTION_TYPE_SELECT {
    86  			assert.Equal(t, request.DataSource, "some_source")
    87  			assert.Equal(t, request.Context["selected_option"], "selected")
    88  		} else {
    89  			assert.Equal(t, request.DataSource, "")
    90  		}
    91  		assert.Equal(t, "foo", request.Context["s"])
    92  		assert.EqualValues(t, 3, request.Context["n"])
    93  		fmt.Fprintf(w, `{"post": {"message": "updated"}, "ephemeral_text": "foo"}`)
    94  	}))
    95  	defer ts.Close()
    96  
    97  	interactivePost := model.Post{
    98  		Message:       "Interactive post",
    99  		ChannelId:     th.BasicChannel.Id,
   100  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   101  		UserId:        th.BasicUser.Id,
   102  		Props: model.StringInterface{
   103  			"attachments": []*model.SlackAttachment{
   104  				{
   105  					Text: "hello",
   106  					Actions: []*model.PostAction{
   107  						{
   108  							Integration: &model.PostActionIntegration{
   109  								Context: model.StringInterface{
   110  									"s": "foo",
   111  									"n": 3,
   112  								},
   113  								URL: ts.URL,
   114  							},
   115  							Name:       "action",
   116  							Type:       "some_type",
   117  							DataSource: "some_source",
   118  						},
   119  					},
   120  				},
   121  			},
   122  		},
   123  	}
   124  
   125  	post, err := th.App.CreatePostAsUser(&interactivePost, "")
   126  	require.Nil(t, err)
   127  
   128  	attachments, ok := post.Props["attachments"].([]*model.SlackAttachment)
   129  	require.True(t, ok)
   130  
   131  	require.NotEmpty(t, attachments[0].Actions)
   132  	require.NotEmpty(t, attachments[0].Actions[0].Id)
   133  
   134  	menuPost := model.Post{
   135  		Message:       "Interactive post",
   136  		ChannelId:     th.BasicChannel.Id,
   137  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   138  		UserId:        th.BasicUser.Id,
   139  		Props: model.StringInterface{
   140  			"attachments": []*model.SlackAttachment{
   141  				{
   142  					Text: "hello",
   143  					Actions: []*model.PostAction{
   144  						{
   145  							Integration: &model.PostActionIntegration{
   146  								Context: model.StringInterface{
   147  									"s": "foo",
   148  									"n": 3,
   149  								},
   150  								URL: ts.URL,
   151  							},
   152  							Name:       "action",
   153  							Type:       model.POST_ACTION_TYPE_SELECT,
   154  							DataSource: "some_source",
   155  						},
   156  					},
   157  				},
   158  			},
   159  		},
   160  	}
   161  
   162  	post2, err := th.App.CreatePostAsUser(&menuPost, "")
   163  	require.Nil(t, err)
   164  
   165  	attachments2, ok := post2.Props["attachments"].([]*model.SlackAttachment)
   166  	require.True(t, ok)
   167  
   168  	require.NotEmpty(t, attachments2[0].Actions)
   169  	require.NotEmpty(t, attachments2[0].Actions[0].Id)
   170  
   171  	clientTriggerId, err := th.App.DoPostAction(post.Id, "notavalidid", th.BasicUser.Id, "")
   172  	require.NotNil(t, err)
   173  	assert.Equal(t, http.StatusNotFound, err.StatusCode)
   174  	assert.True(t, clientTriggerId == "")
   175  
   176  	clientTriggerId, err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "")
   177  	require.Nil(t, err)
   178  	assert.True(t, len(clientTriggerId) == 26)
   179  
   180  	clientTriggerId, err = th.App.DoPostAction(post2.Id, attachments2[0].Actions[0].Id, th.BasicUser.Id, "selected")
   181  	require.Nil(t, err)
   182  	assert.True(t, len(clientTriggerId) == 26)
   183  
   184  	th.App.UpdateConfig(func(cfg *model.Config) {
   185  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = ""
   186  	})
   187  
   188  	_, err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "")
   189  	require.NotNil(t, err)
   190  	require.True(t, strings.Contains(err.Error(), "address forbidden"))
   191  
   192  	interactivePostPlugin := model.Post{
   193  		Message:       "Interactive post",
   194  		ChannelId:     th.BasicChannel.Id,
   195  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   196  		UserId:        th.BasicUser.Id,
   197  		Props: model.StringInterface{
   198  			"attachments": []*model.SlackAttachment{
   199  				{
   200  					Text: "hello",
   201  					Actions: []*model.PostAction{
   202  						{
   203  							Integration: &model.PostActionIntegration{
   204  								Context: model.StringInterface{
   205  									"s": "foo",
   206  									"n": 3,
   207  								},
   208  								URL: ts.URL + "/plugins/myplugin/myaction",
   209  							},
   210  							Name:       "action",
   211  							Type:       "some_type",
   212  							DataSource: "some_source",
   213  						},
   214  					},
   215  				},
   216  			},
   217  		},
   218  	}
   219  
   220  	postplugin, err := th.App.CreatePostAsUser(&interactivePostPlugin, "")
   221  	require.Nil(t, err)
   222  
   223  	attachmentsPlugin, ok := postplugin.Props["attachments"].([]*model.SlackAttachment)
   224  	require.True(t, ok)
   225  
   226  	_, err = th.App.DoPostAction(postplugin.Id, attachmentsPlugin[0].Actions[0].Id, th.BasicUser.Id, "")
   227  	require.Nil(t, err)
   228  
   229  	th.App.UpdateConfig(func(cfg *model.Config) {
   230  		*cfg.ServiceSettings.SiteURL = "http://127.1.1.1"
   231  	})
   232  
   233  	interactivePostSiteURL := model.Post{
   234  		Message:       "Interactive post",
   235  		ChannelId:     th.BasicChannel.Id,
   236  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   237  		UserId:        th.BasicUser.Id,
   238  		Props: model.StringInterface{
   239  			"attachments": []*model.SlackAttachment{
   240  				{
   241  					Text: "hello",
   242  					Actions: []*model.PostAction{
   243  						{
   244  							Integration: &model.PostActionIntegration{
   245  								Context: model.StringInterface{
   246  									"s": "foo",
   247  									"n": 3,
   248  								},
   249  								URL: "http://127.1.1.1/plugins/myplugin/myaction",
   250  							},
   251  							Name:       "action",
   252  							Type:       "some_type",
   253  							DataSource: "some_source",
   254  						},
   255  					},
   256  				},
   257  			},
   258  		},
   259  	}
   260  
   261  	postSiteURL, err := th.App.CreatePostAsUser(&interactivePostSiteURL, "")
   262  	require.Nil(t, err)
   263  
   264  	attachmentsSiteURL, ok := postSiteURL.Props["attachments"].([]*model.SlackAttachment)
   265  	require.True(t, ok)
   266  
   267  	_, err = th.App.DoPostAction(postSiteURL.Id, attachmentsSiteURL[0].Actions[0].Id, th.BasicUser.Id, "")
   268  	require.NotNil(t, err)
   269  	require.False(t, strings.Contains(err.Error(), "address forbidden"))
   270  
   271  	th.App.UpdateConfig(func(cfg *model.Config) {
   272  		*cfg.ServiceSettings.SiteURL = ts.URL + "/subpath"
   273  	})
   274  
   275  	interactivePostSubpath := model.Post{
   276  		Message:       "Interactive post",
   277  		ChannelId:     th.BasicChannel.Id,
   278  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   279  		UserId:        th.BasicUser.Id,
   280  		Props: model.StringInterface{
   281  			"attachments": []*model.SlackAttachment{
   282  				{
   283  					Text: "hello",
   284  					Actions: []*model.PostAction{
   285  						{
   286  							Integration: &model.PostActionIntegration{
   287  								Context: model.StringInterface{
   288  									"s": "foo",
   289  									"n": 3,
   290  								},
   291  								URL: ts.URL + "/subpath/plugins/myplugin/myaction",
   292  							},
   293  							Name:       "action",
   294  							Type:       "some_type",
   295  							DataSource: "some_source",
   296  						},
   297  					},
   298  				},
   299  			},
   300  		},
   301  	}
   302  
   303  	postSubpath, err := th.App.CreatePostAsUser(&interactivePostSubpath, "")
   304  	require.Nil(t, err)
   305  
   306  	attachmentsSubpath, ok := postSubpath.Props["attachments"].([]*model.SlackAttachment)
   307  	require.True(t, ok)
   308  
   309  	_, err = th.App.DoPostAction(postSubpath.Id, attachmentsSubpath[0].Actions[0].Id, th.BasicUser.Id, "")
   310  	require.Nil(t, err)
   311  }
   312  
   313  func TestPostActionProps(t *testing.T) {
   314  	th := Setup(t).InitBasic()
   315  	defer th.TearDown()
   316  
   317  	th.App.UpdateConfig(func(cfg *model.Config) {
   318  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
   319  	})
   320  
   321  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   322  		request := model.PostActionIntegrationRequestFromJson(r.Body)
   323  		assert.NotNil(t, request)
   324  
   325  		fmt.Fprintf(w, `{
   326  			"update": {
   327  				"message": "updated",
   328  				"has_reactions": true,
   329  				"is_pinned": false,
   330  				"props": {
   331  					"from_webhook":true,
   332  					"override_username":"new_override_user",
   333  					"override_icon_url":"new_override_icon",
   334  					"A":"AA"
   335  				}
   336  			},
   337  			"ephemeral_text": "foo"
   338  		}`)
   339  	}))
   340  	defer ts.Close()
   341  
   342  	interactivePost := model.Post{
   343  		Message:       "Interactive post",
   344  		ChannelId:     th.BasicChannel.Id,
   345  		PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
   346  		UserId:        th.BasicUser.Id,
   347  		HasReactions:  false,
   348  		IsPinned:      true,
   349  		Props: model.StringInterface{
   350  			"attachments": []*model.SlackAttachment{
   351  				{
   352  					Text: "hello",
   353  					Actions: []*model.PostAction{
   354  						{
   355  							Integration: &model.PostActionIntegration{
   356  								Context: model.StringInterface{
   357  									"s": "foo",
   358  									"n": 3,
   359  								},
   360  								URL: ts.URL,
   361  							},
   362  							Name:       "action",
   363  							Type:       "some_type",
   364  							DataSource: "some_source",
   365  						},
   366  					},
   367  				},
   368  			},
   369  			"override_icon_url": "old_override_icon",
   370  			"from_webhook":      false,
   371  			"B":                 "BB",
   372  		},
   373  	}
   374  
   375  	post, err := th.App.CreatePostAsUser(&interactivePost, "")
   376  	require.Nil(t, err)
   377  	attachments, ok := post.Props["attachments"].([]*model.SlackAttachment)
   378  	require.True(t, ok)
   379  
   380  	clientTriggerId, err := th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "")
   381  	require.Nil(t, err)
   382  	assert.True(t, len(clientTriggerId) == 26)
   383  
   384  	newPost, err := th.App.Srv.Store.Post().GetSingle(post.Id)
   385  	require.Nil(t, err)
   386  
   387  	assert.True(t, newPost.IsPinned)
   388  	assert.False(t, newPost.HasReactions)
   389  	assert.Nil(t, newPost.Props["B"])
   390  	assert.Nil(t, newPost.Props["override_username"])
   391  	assert.Equal(t, "AA", newPost.Props["A"])
   392  	assert.Equal(t, "old_override_icon", newPost.Props["override_icon_url"])
   393  	assert.Equal(t, false, newPost.Props["from_webhook"])
   394  }
   395  
   396  func TestSubmitInteractiveDialog(t *testing.T) {
   397  	th := Setup(t).InitBasic()
   398  	defer th.TearDown()
   399  
   400  	th.App.UpdateConfig(func(cfg *model.Config) {
   401  		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1"
   402  	})
   403  
   404  	submit := model.SubmitDialogRequest{
   405  		UserId:     th.BasicUser.Id,
   406  		ChannelId:  th.BasicChannel.Id,
   407  		TeamId:     th.BasicTeam.Id,
   408  		CallbackId: "someid",
   409  		State:      "somestate",
   410  		Submission: map[string]interface{}{
   411  			"name1": "value1",
   412  		},
   413  	}
   414  
   415  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   416  		var request model.SubmitDialogRequest
   417  		err := json.NewDecoder(r.Body).Decode(&request)
   418  		require.Nil(t, err)
   419  		assert.NotNil(t, request)
   420  
   421  		assert.Equal(t, request.URL, "")
   422  		assert.Equal(t, request.UserId, submit.UserId)
   423  		assert.Equal(t, request.ChannelId, submit.ChannelId)
   424  		assert.Equal(t, request.TeamId, submit.TeamId)
   425  		assert.Equal(t, request.CallbackId, submit.CallbackId)
   426  		assert.Equal(t, request.State, submit.State)
   427  		val, ok := request.Submission["name1"].(string)
   428  		require.True(t, ok)
   429  		assert.Equal(t, "value1", val)
   430  
   431  		resp := model.SubmitDialogResponse{
   432  			Errors: map[string]string{"name1": "some error"},
   433  		}
   434  
   435  		b, _ := json.Marshal(resp)
   436  
   437  		w.Write(b)
   438  	}))
   439  	defer ts.Close()
   440  
   441  	submit.URL = ts.URL
   442  
   443  	resp, err := th.App.SubmitInteractiveDialog(submit)
   444  	assert.Nil(t, err)
   445  	require.NotNil(t, resp)
   446  	assert.Equal(t, "some error", resp.Errors["name1"])
   447  
   448  	submit.URL = ""
   449  	resp, err = th.App.SubmitInteractiveDialog(submit)
   450  	assert.NotNil(t, err)
   451  	assert.Nil(t, resp)
   452  }