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 }