github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/plugin_test.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package app 5 6 import ( 7 "bytes" 8 "crypto/sha256" 9 "encoding/base64" 10 "fmt" 11 "io/ioutil" 12 "net/http" 13 "net/http/httptest" 14 "os" 15 "path/filepath" 16 "testing" 17 "time" 18 19 "github.com/gorilla/mux" 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 23 "github.com/mattermost/mattermost-server/v5/mlog" 24 "github.com/mattermost/mattermost-server/v5/model" 25 "github.com/mattermost/mattermost-server/v5/plugin" 26 "github.com/mattermost/mattermost-server/v5/testlib" 27 "github.com/mattermost/mattermost-server/v5/utils" 28 "github.com/mattermost/mattermost-server/v5/utils/fileutils" 29 ) 30 31 func getHashedKey(key string) string { 32 hash := sha256.New() 33 hash.Write([]byte(key)) 34 return base64.StdEncoding.EncodeToString(hash.Sum(nil)) 35 } 36 37 func TestPluginKeyValueStore(t *testing.T) { 38 th := Setup(t) 39 defer th.TearDown() 40 41 pluginId := "testpluginid" 42 43 defer func() { 44 assert.Nil(t, th.App.DeletePluginKey(pluginId, "key")) 45 assert.Nil(t, th.App.DeletePluginKey(pluginId, "key2")) 46 assert.Nil(t, th.App.DeletePluginKey(pluginId, "key3")) 47 assert.Nil(t, th.App.DeletePluginKey(pluginId, "key4")) 48 }() 49 50 assert.Nil(t, th.App.SetPluginKey(pluginId, "key", []byte("test"))) 51 ret, err := th.App.GetPluginKey(pluginId, "key") 52 assert.Nil(t, err) 53 assert.Equal(t, []byte("test"), ret) 54 55 // Test inserting over existing entries 56 assert.Nil(t, th.App.SetPluginKey(pluginId, "key", []byte("test2"))) 57 ret, err = th.App.GetPluginKey(pluginId, "key") 58 assert.Nil(t, err) 59 assert.Equal(t, []byte("test2"), ret) 60 61 // Test getting non-existent key 62 ret, err = th.App.GetPluginKey(pluginId, "notakey") 63 assert.Nil(t, err) 64 assert.Nil(t, ret) 65 66 // Test deleting non-existent keys. 67 assert.Nil(t, th.App.DeletePluginKey(pluginId, "notrealkey")) 68 69 // Verify behaviour for the old approach that involved storing the hashed keys. 70 hashedKey2 := getHashedKey("key2") 71 kv := &model.PluginKeyValue{ 72 PluginId: pluginId, 73 Key: hashedKey2, 74 Value: []byte("test"), 75 ExpireAt: 0, 76 } 77 78 _, err = th.App.Srv().Store.Plugin().SaveOrUpdate(kv) 79 assert.Nil(t, err) 80 81 // Test fetch by keyname (this key does not exist but hashed key will be used for lookup) 82 ret, err = th.App.GetPluginKey(pluginId, "key2") 83 assert.Nil(t, err) 84 assert.Equal(t, kv.Value, ret) 85 86 // Test fetch by hashed keyname 87 ret, err = th.App.GetPluginKey(pluginId, hashedKey2) 88 assert.Nil(t, err) 89 assert.Equal(t, kv.Value, ret) 90 91 // Test ListKeys 92 assert.Nil(t, th.App.SetPluginKey(pluginId, "key3", []byte("test3"))) 93 assert.Nil(t, th.App.SetPluginKey(pluginId, "key4", []byte("test4"))) 94 95 list, err := th.App.ListPluginKeys(pluginId, 0, 1) 96 assert.Nil(t, err) 97 assert.Equal(t, []string{"key"}, list) 98 99 list, err = th.App.ListPluginKeys(pluginId, 1, 1) 100 assert.Nil(t, err) 101 assert.Equal(t, []string{"key3"}, list) 102 103 list, err = th.App.ListPluginKeys(pluginId, 0, 4) 104 assert.Nil(t, err) 105 assert.Equal(t, []string{"key", "key3", "key4", hashedKey2}, list) 106 107 list, err = th.App.ListPluginKeys(pluginId, 0, 2) 108 assert.Nil(t, err) 109 assert.Equal(t, []string{"key", "key3"}, list) 110 111 list, err = th.App.ListPluginKeys(pluginId, 1, 2) 112 assert.Nil(t, err) 113 assert.Equal(t, []string{"key4", hashedKey2}, list) 114 115 list, err = th.App.ListPluginKeys(pluginId, 2, 2) 116 assert.Nil(t, err) 117 assert.Equal(t, []string{}, list) 118 119 // List Keys bad input 120 list, err = th.App.ListPluginKeys(pluginId, 0, 0) 121 assert.Nil(t, err) 122 assert.Equal(t, []string{"key", "key3", "key4", hashedKey2}, list) 123 124 list, err = th.App.ListPluginKeys(pluginId, 0, -1) 125 assert.Nil(t, err) 126 assert.Equal(t, []string{"key", "key3", "key4", hashedKey2}, list) 127 128 list, err = th.App.ListPluginKeys(pluginId, -1, 1) 129 assert.Nil(t, err) 130 assert.Equal(t, []string{"key"}, list) 131 132 list, err = th.App.ListPluginKeys(pluginId, -1, 0) 133 assert.Nil(t, err) 134 assert.Equal(t, []string{"key", "key3", "key4", hashedKey2}, list) 135 } 136 137 func TestPluginKeyValueStoreCompareAndSet(t *testing.T) { 138 th := Setup(t) 139 defer th.TearDown() 140 141 pluginId := "testpluginid" 142 143 defer func() { 144 assert.Nil(t, th.App.DeletePluginKey(pluginId, "key")) 145 }() 146 147 // Set using Set api for key2 148 assert.Nil(t, th.App.SetPluginKey(pluginId, "key2", []byte("test"))) 149 ret, err := th.App.GetPluginKey(pluginId, "key2") 150 assert.Nil(t, err) 151 assert.Equal(t, []byte("test"), ret) 152 153 // Attempt to insert value for key2 154 updated, err := th.App.CompareAndSetPluginKey(pluginId, "key2", nil, []byte("test2")) 155 assert.Nil(t, err) 156 assert.False(t, updated) 157 ret, err = th.App.GetPluginKey(pluginId, "key2") 158 assert.Nil(t, err) 159 assert.Equal(t, []byte("test"), ret) 160 161 // Insert new value for key 162 updated, err = th.App.CompareAndSetPluginKey(pluginId, "key", nil, []byte("test")) 163 assert.Nil(t, err) 164 assert.True(t, updated) 165 ret, err = th.App.GetPluginKey(pluginId, "key") 166 assert.Nil(t, err) 167 assert.Equal(t, []byte("test"), ret) 168 169 // Should fail to insert again 170 updated, err = th.App.CompareAndSetPluginKey(pluginId, "key", nil, []byte("test3")) 171 assert.Nil(t, err) 172 assert.False(t, updated) 173 ret, err = th.App.GetPluginKey(pluginId, "key") 174 assert.Nil(t, err) 175 assert.Equal(t, []byte("test"), ret) 176 177 // Test updating using incorrect old value 178 updated, err = th.App.CompareAndSetPluginKey(pluginId, "key", []byte("oldvalue"), []byte("test3")) 179 assert.Nil(t, err) 180 assert.False(t, updated) 181 ret, err = th.App.GetPluginKey(pluginId, "key") 182 assert.Nil(t, err) 183 assert.Equal(t, []byte("test"), ret) 184 185 // Test updating using correct old value 186 updated, err = th.App.CompareAndSetPluginKey(pluginId, "key", []byte("test"), []byte("test2")) 187 assert.Nil(t, err) 188 assert.True(t, updated) 189 ret, err = th.App.GetPluginKey(pluginId, "key") 190 assert.Nil(t, err) 191 assert.Equal(t, []byte("test2"), ret) 192 } 193 194 func TestPluginKeyValueStoreSetWithOptionsJSON(t *testing.T) { 195 pluginId := "testpluginid" 196 197 t.Run("storing a value without providing options works", func(t *testing.T) { 198 th := Setup(t) 199 defer th.TearDown() 200 201 result, err := th.App.SetPluginKeyWithOptions(pluginId, "key", []byte("value-1"), model.PluginKVSetOptions{}) 202 assert.True(t, result) 203 assert.Nil(t, err) 204 205 // and I can get it back! 206 ret, err := th.App.GetPluginKey(pluginId, "key") 207 assert.Nil(t, err) 208 assert.Equal(t, []byte(`value-1`), ret) 209 }) 210 211 t.Run("test that setting it atomic when it doesn't match doesn't change anything", func(t *testing.T) { 212 th := Setup(t) 213 defer th.TearDown() 214 215 err := th.App.SetPluginKey(pluginId, "key", []byte("value-1")) 216 require.Nil(t, err) 217 218 result, err := th.App.SetPluginKeyWithOptions(pluginId, "key", []byte("value-3"), model.PluginKVSetOptions{ 219 Atomic: true, 220 OldValue: []byte("value-2"), 221 }) 222 assert.False(t, result) 223 assert.Nil(t, err) 224 225 // test that the value didn't change 226 ret, err := th.App.GetPluginKey(pluginId, "key") 227 assert.Nil(t, err) 228 assert.Equal(t, []byte(`value-1`), ret) 229 }) 230 231 t.Run("test the atomic change with the proper old value", func(t *testing.T) { 232 th := Setup(t) 233 defer th.TearDown() 234 235 err := th.App.SetPluginKey(pluginId, "key", []byte("value-2")) 236 require.Nil(t, err) 237 238 result, err := th.App.SetPluginKeyWithOptions(pluginId, "key", []byte("value-3"), model.PluginKVSetOptions{ 239 Atomic: true, 240 OldValue: []byte("value-2"), 241 }) 242 assert.True(t, result) 243 assert.Nil(t, err) 244 245 // test that the value did change 246 ret, err := th.App.GetPluginKey(pluginId, "key") 247 assert.Nil(t, err) 248 assert.Equal(t, []byte(`value-3`), ret) 249 }) 250 251 t.Run("when new value is nil and old value matches with the current, it should delete the currently set value", func(t *testing.T) { 252 th := Setup(t) 253 defer th.TearDown() 254 255 // first set a value. 256 result, err := th.App.SetPluginKeyWithOptions(pluginId, "nil-test-key-2", []byte("value-1"), model.PluginKVSetOptions{}) 257 require.Nil(t, err) 258 require.True(t, result) 259 260 // now it should delete the set value. 261 result, err = th.App.SetPluginKeyWithOptions(pluginId, "nil-test-key-2", nil, model.PluginKVSetOptions{ 262 Atomic: true, 263 OldValue: []byte("value-1"), 264 }) 265 assert.Nil(t, err) 266 assert.True(t, result) 267 268 ret, err := th.App.GetPluginKey(pluginId, "nil-test-key-2") 269 assert.Nil(t, err) 270 assert.Nil(t, ret) 271 }) 272 273 t.Run("when new value is nil and there is a value set for the key already, it should delete the currently set value", func(t *testing.T) { 274 th := Setup(t) 275 defer th.TearDown() 276 277 // first set a value. 278 result, err := th.App.SetPluginKeyWithOptions(pluginId, "nil-test-key-3", []byte("value-1"), model.PluginKVSetOptions{}) 279 require.Nil(t, err) 280 require.True(t, result) 281 282 // now it should delete the set value. 283 result, err = th.App.SetPluginKeyWithOptions(pluginId, "nil-test-key-3", nil, model.PluginKVSetOptions{}) 284 assert.Nil(t, err) 285 assert.True(t, result) 286 287 // verify a nil value is returned 288 ret, err := th.App.GetPluginKey(pluginId, "nil-test-key-3") 289 assert.Nil(t, err) 290 assert.Nil(t, ret) 291 292 // verify the row is actually gone 293 list, err := th.App.ListPluginKeys(pluginId, 0, 1) 294 assert.Nil(t, err) 295 assert.Empty(t, list) 296 }) 297 298 t.Run("when old value is nil and there is no value set for the key before, it should set the new value", func(t *testing.T) { 299 th := Setup(t) 300 defer th.TearDown() 301 302 result, err := th.App.SetPluginKeyWithOptions(pluginId, "nil-test-key-4", []byte("value-1"), model.PluginKVSetOptions{ 303 Atomic: true, 304 OldValue: nil, 305 }) 306 assert.Nil(t, err) 307 assert.True(t, result) 308 309 ret, err := th.App.GetPluginKey(pluginId, "nil-test-key-4") 310 assert.Nil(t, err) 311 assert.Equal(t, []byte("value-1"), ret) 312 }) 313 314 t.Run("test that value is set and unset with ExpireInSeconds", func(t *testing.T) { 315 th := Setup(t) 316 defer th.TearDown() 317 318 result, err := th.App.SetPluginKeyWithOptions(pluginId, "key", []byte("value-1"), model.PluginKVSetOptions{ 319 ExpireInSeconds: 1, 320 }) 321 assert.True(t, result) 322 assert.Nil(t, err) 323 324 // test that the value is set 325 ret, err := th.App.GetPluginKey(pluginId, "key") 326 assert.Nil(t, err) 327 assert.Equal(t, []byte(`value-1`), ret) 328 329 // test that the value is not longer 330 time.Sleep(1500 * time.Millisecond) 331 332 ret, err = th.App.GetPluginKey(pluginId, "key") 333 assert.Nil(t, err) 334 assert.Nil(t, ret) 335 }) 336 } 337 338 func TestServePluginRequest(t *testing.T) { 339 th := Setup(t) 340 defer th.TearDown() 341 342 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.PluginSettings.Enable = false }) 343 344 w := httptest.NewRecorder() 345 r := httptest.NewRequest("GET", "/plugins/foo/bar", nil) 346 th.App.ServePluginRequest(w, r) 347 assert.Equal(t, http.StatusNotImplemented, w.Result().StatusCode) 348 } 349 350 func TestPrivateServePluginRequest(t *testing.T) { 351 th := Setup(t) 352 defer th.TearDown() 353 354 testCases := []struct { 355 Description string 356 ConfigFunc func(cfg *model.Config) 357 URL string 358 ExpectedURL string 359 }{ 360 { 361 "no subpath", 362 func(cfg *model.Config) {}, 363 "/plugins/id/endpoint", 364 "/endpoint", 365 }, 366 { 367 "subpath", 368 func(cfg *model.Config) { *cfg.ServiceSettings.SiteURL += "/subpath" }, 369 "/subpath/plugins/id/endpoint", 370 "/endpoint", 371 }, 372 } 373 374 for _, testCase := range testCases { 375 t.Run(testCase.Description, func(t *testing.T) { 376 th.App.UpdateConfig(testCase.ConfigFunc) 377 expectedBody := []byte("body") 378 request := httptest.NewRequest(http.MethodGet, testCase.URL, bytes.NewReader(expectedBody)) 379 recorder := httptest.NewRecorder() 380 381 handler := func(context *plugin.Context, w http.ResponseWriter, r *http.Request) { 382 assert.Equal(t, testCase.ExpectedURL, r.URL.Path) 383 384 body, _ := ioutil.ReadAll(r.Body) 385 assert.Equal(t, expectedBody, body) 386 } 387 388 request = mux.SetURLVars(request, map[string]string{"plugin_id": "id"}) 389 390 th.App.servePluginRequest(recorder, request, handler) 391 }) 392 } 393 394 } 395 396 func TestHandlePluginRequest(t *testing.T) { 397 th := Setup(t).InitBasic() 398 defer th.TearDown() 399 400 th.App.UpdateConfig(func(cfg *model.Config) { 401 *cfg.PluginSettings.Enable = false 402 *cfg.ServiceSettings.EnableUserAccessTokens = true 403 }) 404 405 token, err := th.App.CreateUserAccessToken(&model.UserAccessToken{ 406 UserId: th.BasicUser.Id, 407 }) 408 require.Nil(t, err) 409 410 var assertions func(*http.Request) 411 router := mux.NewRouter() 412 router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}/{anything:.*}", func(_ http.ResponseWriter, r *http.Request) { 413 th.App.servePluginRequest(nil, r, func(_ *plugin.Context, _ http.ResponseWriter, r *http.Request) { 414 assertions(r) 415 }) 416 }) 417 418 r := httptest.NewRequest("GET", "/plugins/foo/bar", nil) 419 r.Header.Add("Authorization", "Bearer "+token.Token) 420 assertions = func(r *http.Request) { 421 assert.Equal(t, "/bar", r.URL.Path) 422 assert.Equal(t, th.BasicUser.Id, r.Header.Get("Mattermost-User-Id")) 423 } 424 router.ServeHTTP(nil, r) 425 426 r = httptest.NewRequest("GET", "/plugins/foo/bar?a=b&access_token="+token.Token+"&c=d", nil) 427 assertions = func(r *http.Request) { 428 assert.Equal(t, "/bar", r.URL.Path) 429 assert.Equal(t, "a=b&c=d", r.URL.RawQuery) 430 assert.Equal(t, th.BasicUser.Id, r.Header.Get("Mattermost-User-Id")) 431 } 432 router.ServeHTTP(nil, r) 433 434 r = httptest.NewRequest("GET", "/plugins/foo/bar?a=b&access_token=asdf&c=d", nil) 435 assertions = func(r *http.Request) { 436 assert.Equal(t, "/bar", r.URL.Path) 437 assert.Equal(t, "a=b&c=d", r.URL.RawQuery) 438 assert.Empty(t, r.Header.Get("Mattermost-User-Id")) 439 } 440 router.ServeHTTP(nil, r) 441 } 442 443 func TestGetPluginStatusesDisabled(t *testing.T) { 444 th := Setup(t) 445 defer th.TearDown() 446 447 th.App.UpdateConfig(func(cfg *model.Config) { 448 *cfg.PluginSettings.Enable = false 449 }) 450 451 _, err := th.App.GetPluginStatuses() 452 require.NotNil(t, err) 453 require.EqualError(t, err, "GetPluginStatuses: Plugins have been disabled. Please check your logs for details., ") 454 } 455 456 func TestGetPluginStatuses(t *testing.T) { 457 th := Setup(t) 458 defer th.TearDown() 459 460 th.App.UpdateConfig(func(cfg *model.Config) { 461 *cfg.PluginSettings.Enable = true 462 }) 463 464 pluginStatuses, err := th.App.GetPluginStatuses() 465 require.Nil(t, err) 466 require.NotNil(t, pluginStatuses) 467 } 468 469 func TestPluginSync(t *testing.T) { 470 th := Setup(t) 471 defer th.TearDown() 472 473 testCases := []struct { 474 Description string 475 ConfigFunc func(cfg *model.Config) 476 }{ 477 { 478 "local", 479 func(cfg *model.Config) { 480 cfg.FileSettings.DriverName = model.NewString(model.IMAGE_DRIVER_LOCAL) 481 }, 482 }, 483 { 484 "s3", 485 func(cfg *model.Config) { 486 s3Host := os.Getenv("CI_MINIO_HOST") 487 if s3Host == "" { 488 s3Host = "localhost" 489 } 490 491 s3Port := os.Getenv("CI_MINIO_PORT") 492 if s3Port == "" { 493 s3Port = "9000" 494 } 495 496 s3Endpoint := fmt.Sprintf("%s:%s", s3Host, s3Port) 497 cfg.FileSettings.DriverName = model.NewString(model.IMAGE_DRIVER_S3) 498 cfg.FileSettings.AmazonS3AccessKeyId = model.NewString(model.MINIO_ACCESS_KEY) 499 cfg.FileSettings.AmazonS3SecretAccessKey = model.NewString(model.MINIO_SECRET_KEY) 500 cfg.FileSettings.AmazonS3Bucket = model.NewString(model.MINIO_BUCKET) 501 cfg.FileSettings.AmazonS3PathPrefix = model.NewString("") 502 cfg.FileSettings.AmazonS3Endpoint = model.NewString(s3Endpoint) 503 cfg.FileSettings.AmazonS3Region = model.NewString("") 504 cfg.FileSettings.AmazonS3SSL = model.NewBool(false) 505 506 }, 507 }, 508 } 509 510 for _, testCase := range testCases { 511 t.Run(testCase.Description, func(t *testing.T) { 512 th.App.UpdateConfig(func(cfg *model.Config) { 513 *cfg.PluginSettings.Enable = true 514 testCase.ConfigFunc(cfg) 515 }) 516 517 env := th.App.GetPluginsEnvironment() 518 require.NotNil(t, env) 519 520 path, _ := fileutils.FindDir("tests") 521 522 t.Run("new bundle in the file store", func(t *testing.T) { 523 th.App.UpdateConfig(func(cfg *model.Config) { 524 *cfg.PluginSettings.RequirePluginSignature = false 525 }) 526 527 fileReader, err := os.Open(filepath.Join(path, "testplugin.tar.gz")) 528 require.NoError(t, err) 529 defer fileReader.Close() 530 531 _, appErr := th.App.WriteFile(fileReader, th.App.getBundleStorePath("testplugin")) 532 checkNoError(t, appErr) 533 534 appErr = th.App.SyncPlugins() 535 checkNoError(t, appErr) 536 537 // Check if installed 538 pluginStatus, err := env.Statuses() 539 require.Nil(t, err) 540 require.Len(t, pluginStatus, 1) 541 require.Equal(t, pluginStatus[0].PluginId, "testplugin") 542 }) 543 544 t.Run("bundle removed from the file store", func(t *testing.T) { 545 th.App.UpdateConfig(func(cfg *model.Config) { 546 *cfg.PluginSettings.RequirePluginSignature = false 547 }) 548 549 appErr := th.App.RemoveFile(th.App.getBundleStorePath("testplugin")) 550 checkNoError(t, appErr) 551 552 appErr = th.App.SyncPlugins() 553 checkNoError(t, appErr) 554 555 // Check if removed 556 pluginStatus, err := env.Statuses() 557 require.Nil(t, err) 558 require.Empty(t, pluginStatus) 559 }) 560 561 t.Run("plugin signatures required, no signature", func(t *testing.T) { 562 th.App.UpdateConfig(func(cfg *model.Config) { 563 *cfg.PluginSettings.RequirePluginSignature = true 564 }) 565 566 pluginFileReader, err := os.Open(filepath.Join(path, "testplugin.tar.gz")) 567 require.NoError(t, err) 568 defer pluginFileReader.Close() 569 _, appErr := th.App.WriteFile(pluginFileReader, th.App.getBundleStorePath("testplugin")) 570 checkNoError(t, appErr) 571 572 appErr = th.App.SyncPlugins() 573 checkNoError(t, appErr) 574 pluginStatus, err := env.Statuses() 575 require.Nil(t, err) 576 require.Len(t, pluginStatus, 0) 577 }) 578 579 t.Run("plugin signatures required, wrong signature", func(t *testing.T) { 580 th.App.UpdateConfig(func(cfg *model.Config) { 581 *cfg.PluginSettings.RequirePluginSignature = true 582 }) 583 584 signatureFileReader, err := os.Open(filepath.Join(path, "testplugin2.tar.gz.sig")) 585 require.NoError(t, err) 586 defer signatureFileReader.Close() 587 _, appErr := th.App.WriteFile(signatureFileReader, th.App.getSignatureStorePath("testplugin")) 588 checkNoError(t, appErr) 589 590 appErr = th.App.SyncPlugins() 591 checkNoError(t, appErr) 592 593 pluginStatus, err := env.Statuses() 594 require.Nil(t, err) 595 require.Len(t, pluginStatus, 0) 596 }) 597 598 t.Run("plugin signatures required, correct signature", func(t *testing.T) { 599 th.App.UpdateConfig(func(cfg *model.Config) { 600 *cfg.PluginSettings.RequirePluginSignature = true 601 }) 602 603 key, err := os.Open(filepath.Join(path, "development-private-key.asc")) 604 require.NoError(t, err) 605 appErr := th.App.AddPublicKey("pub_key", key) 606 checkNoError(t, appErr) 607 608 signatureFileReader, err := os.Open(filepath.Join(path, "testplugin.tar.gz.sig")) 609 require.NoError(t, err) 610 defer signatureFileReader.Close() 611 _, appErr = th.App.WriteFile(signatureFileReader, th.App.getSignatureStorePath("testplugin")) 612 checkNoError(t, appErr) 613 614 appErr = th.App.SyncPlugins() 615 checkNoError(t, appErr) 616 617 pluginStatus, err := env.Statuses() 618 require.Nil(t, err) 619 require.Len(t, pluginStatus, 1) 620 require.Equal(t, pluginStatus[0].PluginId, "testplugin") 621 622 appErr = th.App.DeletePublicKey("pub_key") 623 checkNoError(t, appErr) 624 625 appErr = th.App.RemovePlugin("testplugin") 626 checkNoError(t, appErr) 627 }) 628 }) 629 } 630 } 631 632 func TestPluginPanicLogs(t *testing.T) { 633 t.Run("should panic", func(t *testing.T) { 634 th := Setup(t).InitBasic() 635 defer th.TearDown() 636 tearDown, _, _ := SetAppEnvironmentWithPlugins(t, []string{ 637 ` 638 package main 639 640 import ( 641 "github.com/mattermost/mattermost-server/v5/plugin" 642 "github.com/mattermost/mattermost-server/v5/model" 643 ) 644 645 type MyPlugin struct { 646 plugin.MattermostPlugin 647 } 648 649 func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { 650 panic("some text from panic") 651 return nil, "" 652 } 653 654 func main() { 655 plugin.ClientMain(&MyPlugin{}) 656 } 657 `, 658 }, th.App, th.App.NewPluginAPI) 659 defer tearDown() 660 661 post := &model.Post{ 662 UserId: th.BasicUser.Id, 663 ChannelId: th.BasicChannel.Id, 664 Message: "message_", 665 CreateAt: model.GetMillis() - 10000, 666 } 667 _, err := th.App.CreatePost(post, th.BasicChannel, false, true) 668 assert.Nil(t, err) 669 670 testlib.AssertLog(t, th.LogBuffer, mlog.LevelDebug, "panic: some text from panic") 671 }) 672 } 673 674 func TestProcessPrepackagedPlugins(t *testing.T) { 675 th := Setup(t) 676 defer th.TearDown() 677 678 testsPath, _ := fileutils.FindDir("tests") 679 prepackagedPluginsPath := filepath.Join(testsPath, prepackagedPluginsDir) 680 fileErr := os.Mkdir(prepackagedPluginsPath, os.ModePerm) 681 require.NoError(t, fileErr) 682 defer os.RemoveAll(prepackagedPluginsPath) 683 684 prepackagedPluginsDir, found := fileutils.FindDir(prepackagedPluginsPath) 685 require.True(t, found, "failed to find prepackaged plugins directory") 686 687 testPluginPath := filepath.Join(testsPath, "testplugin.tar.gz") 688 fileErr = utils.CopyFile(testPluginPath, filepath.Join(prepackagedPluginsDir, "testplugin.tar.gz")) 689 require.NoError(t, fileErr) 690 691 t.Run("automatic, enabled plugin, no signature", func(t *testing.T) { 692 // Install the plugin and enable 693 pluginBytes, err := ioutil.ReadFile(testPluginPath) 694 require.NoError(t, err) 695 require.NotNil(t, pluginBytes) 696 697 manifest, appErr := th.App.installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways) 698 require.Nil(t, appErr) 699 require.Equal(t, "testplugin", manifest.Id) 700 701 env := th.App.GetPluginsEnvironment() 702 703 activatedManifest, activated, err := env.Activate(manifest.Id) 704 require.NoError(t, err) 705 require.True(t, activated) 706 require.Equal(t, manifest, activatedManifest) 707 708 th.App.UpdateConfig(func(cfg *model.Config) { 709 *cfg.PluginSettings.Enable = true 710 *cfg.PluginSettings.AutomaticPrepackagedPlugins = true 711 }) 712 713 plugins := th.App.processPrepackagedPlugins(prepackagedPluginsDir) 714 require.Len(t, plugins, 1) 715 require.Equal(t, plugins[0].Manifest.Id, "testplugin") 716 require.Empty(t, plugins[0].Signature, 0) 717 718 pluginStatus, err := env.Statuses() 719 require.NoError(t, err) 720 require.Len(t, pluginStatus, 1) 721 require.Equal(t, pluginStatus[0].PluginId, "testplugin") 722 723 appErr = th.App.RemovePlugin("testplugin") 724 checkNoError(t, appErr) 725 726 pluginStatus, err = env.Statuses() 727 require.NoError(t, err) 728 require.Len(t, pluginStatus, 0) 729 }) 730 731 t.Run("automatic, not enabled plugin", func(t *testing.T) { 732 th.App.UpdateConfig(func(cfg *model.Config) { 733 *cfg.PluginSettings.Enable = true 734 *cfg.PluginSettings.AutomaticPrepackagedPlugins = true 735 }) 736 737 env := th.App.GetPluginsEnvironment() 738 739 plugins := th.App.processPrepackagedPlugins(prepackagedPluginsDir) 740 require.Len(t, plugins, 1) 741 require.Equal(t, plugins[0].Manifest.Id, "testplugin") 742 require.Empty(t, plugins[0].Signature, 0) 743 744 pluginStatus, err := env.Statuses() 745 require.NoError(t, err) 746 require.Empty(t, pluginStatus, 0) 747 }) 748 749 t.Run("automatic, multiple plugins with signatures, not enabled", func(t *testing.T) { 750 th.App.UpdateConfig(func(cfg *model.Config) { 751 *cfg.PluginSettings.Enable = true 752 *cfg.PluginSettings.AutomaticPrepackagedPlugins = true 753 }) 754 755 env := th.App.GetPluginsEnvironment() 756 757 // Add signature 758 testPluginSignaturePath := filepath.Join(testsPath, "testplugin.tar.gz.sig") 759 err := utils.CopyFile(testPluginSignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin.tar.gz.sig")) 760 require.NoError(t, err) 761 762 // Add second plugin 763 testPlugin2Path := filepath.Join(testsPath, "testplugin2.tar.gz") 764 err = utils.CopyFile(testPlugin2Path, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz")) 765 require.NoError(t, err) 766 767 testPlugin2SignaturePath := filepath.Join(testsPath, "testplugin2.tar.gz.sig") 768 err = utils.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig")) 769 require.NoError(t, err) 770 771 plugins := th.App.processPrepackagedPlugins(prepackagedPluginsDir) 772 require.Len(t, plugins, 2) 773 require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id) 774 require.NotEmpty(t, plugins[0].Signature) 775 require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[1].Manifest.Id) 776 require.NotEmpty(t, plugins[1].Signature) 777 778 pluginStatus, err := env.Statuses() 779 require.NoError(t, err) 780 require.Len(t, pluginStatus, 0) 781 }) 782 783 t.Run("automatic, multiple plugins with signatures, one enabled", func(t *testing.T) { 784 th.App.UpdateConfig(func(cfg *model.Config) { 785 *cfg.PluginSettings.Enable = true 786 *cfg.PluginSettings.AutomaticPrepackagedPlugins = true 787 }) 788 789 env := th.App.GetPluginsEnvironment() 790 791 // Add signature 792 testPluginSignaturePath := filepath.Join(testsPath, "testplugin.tar.gz.sig") 793 err := utils.CopyFile(testPluginSignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin.tar.gz.sig")) 794 require.NoError(t, err) 795 796 // Install first plugin and enable 797 pluginBytes, err := ioutil.ReadFile(testPluginPath) 798 require.NoError(t, err) 799 require.NotNil(t, pluginBytes) 800 801 manifest, appErr := th.App.installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways) 802 require.Nil(t, appErr) 803 require.Equal(t, "testplugin", manifest.Id) 804 805 activatedManifest, activated, err := env.Activate(manifest.Id) 806 require.NoError(t, err) 807 require.True(t, activated) 808 require.Equal(t, manifest, activatedManifest) 809 810 // Add second plugin 811 testPlugin2Path := filepath.Join(testsPath, "testplugin2.tar.gz") 812 err = utils.CopyFile(testPlugin2Path, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz")) 813 require.NoError(t, err) 814 815 testPlugin2SignaturePath := filepath.Join(testsPath, "testplugin2.tar.gz.sig") 816 err = utils.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig")) 817 require.NoError(t, err) 818 819 plugins := th.App.processPrepackagedPlugins(prepackagedPluginsDir) 820 require.Len(t, plugins, 2) 821 require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id) 822 require.NotEmpty(t, plugins[0].Signature) 823 require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[1].Manifest.Id) 824 require.NotEmpty(t, plugins[1].Signature) 825 826 pluginStatus, err := env.Statuses() 827 require.NoError(t, err) 828 require.Len(t, pluginStatus, 1) 829 require.Equal(t, pluginStatus[0].PluginId, "testplugin") 830 831 appErr = th.App.RemovePlugin("testplugin") 832 checkNoError(t, appErr) 833 834 pluginStatus, err = env.Statuses() 835 require.NoError(t, err) 836 require.Len(t, pluginStatus, 0) 837 }) 838 839 t.Run("non-automatic, multiple plugins", func(t *testing.T) { 840 th.App.UpdateConfig(func(cfg *model.Config) { 841 *cfg.PluginSettings.Enable = true 842 *cfg.PluginSettings.AutomaticPrepackagedPlugins = false 843 }) 844 845 env := th.App.GetPluginsEnvironment() 846 847 testPlugin2Path := filepath.Join(testsPath, "testplugin2.tar.gz") 848 err := utils.CopyFile(testPlugin2Path, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz")) 849 require.NoError(t, err) 850 851 testPlugin2SignaturePath := filepath.Join(testsPath, "testplugin2.tar.gz.sig") 852 err = utils.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig")) 853 require.NoError(t, err) 854 855 plugins := th.App.processPrepackagedPlugins(prepackagedPluginsDir) 856 require.Len(t, plugins, 2) 857 require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id) 858 require.NotEmpty(t, plugins[0].Signature) 859 require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[1].Manifest.Id) 860 require.NotEmpty(t, plugins[1].Signature) 861 862 pluginStatus, err := env.Statuses() 863 require.NoError(t, err) 864 require.Len(t, pluginStatus, 0) 865 }) 866 }