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  }