github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/plugin.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  	"encoding/base64"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"net/http"
    12  	"os"
    13  	"path/filepath"
    14  	"sort"
    15  	"strings"
    16  	"sync"
    17  
    18  	"github.com/mattermost/mattermost-server/v5/mlog"
    19  	"github.com/mattermost/mattermost-server/v5/model"
    20  	"github.com/mattermost/mattermost-server/v5/plugin"
    21  	"github.com/mattermost/mattermost-server/v5/services/filesstore"
    22  	"github.com/mattermost/mattermost-server/v5/services/marketplace"
    23  	"github.com/mattermost/mattermost-server/v5/utils/fileutils"
    24  
    25  	"github.com/blang/semver"
    26  	svg "github.com/h2non/go-is-svg"
    27  	"github.com/pkg/errors"
    28  )
    29  
    30  const prepackagedPluginsDir = "prepackaged_plugins"
    31  
    32  type pluginSignaturePath struct {
    33  	pluginId      string
    34  	path          string
    35  	signaturePath string
    36  }
    37  
    38  // GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and
    39  // initialized.
    40  //
    41  // To get the plugins environment when the plugins are disabled, manually acquire the plugins
    42  // lock instead.
    43  func (s *Server) GetPluginsEnvironment() *plugin.Environment {
    44  	if !*s.Config().PluginSettings.Enable {
    45  		return nil
    46  	}
    47  
    48  	s.PluginsLock.RLock()
    49  	defer s.PluginsLock.RUnlock()
    50  
    51  	return s.PluginsEnvironment
    52  }
    53  
    54  // GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and
    55  // initialized.
    56  //
    57  // To get the plugins environment when the plugins are disabled, manually acquire the plugins
    58  // lock instead.
    59  func (a *App) GetPluginsEnvironment() *plugin.Environment {
    60  	return a.Srv().GetPluginsEnvironment()
    61  }
    62  
    63  func (a *App) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) {
    64  	a.Srv().PluginsLock.Lock()
    65  	defer a.Srv().PluginsLock.Unlock()
    66  
    67  	a.Srv().PluginsEnvironment = pluginsEnvironment
    68  }
    69  
    70  func (a *App) SyncPluginsActiveState() {
    71  	// Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
    72  	a.Srv().PluginsLock.RLock()
    73  	pluginsEnvironment := a.Srv().PluginsEnvironment
    74  	a.Srv().PluginsLock.RUnlock()
    75  
    76  	if pluginsEnvironment == nil {
    77  		return
    78  	}
    79  
    80  	config := a.Config().PluginSettings
    81  
    82  	if *config.Enable {
    83  		availablePlugins, err := pluginsEnvironment.Available()
    84  		if err != nil {
    85  			a.Log().Error("Unable to get available plugins", mlog.Err(err))
    86  			return
    87  		}
    88  
    89  		// Deactivate any plugins that have been disabled.
    90  		for _, plugin := range availablePlugins {
    91  			// Determine if plugin is enabled
    92  			pluginId := plugin.Manifest.Id
    93  			pluginEnabled := false
    94  			if state, ok := config.PluginStates[pluginId]; ok {
    95  				pluginEnabled = state.Enable
    96  			}
    97  
    98  			// If it's not enabled we need to deactivate it
    99  			if !pluginEnabled {
   100  				deactivated := pluginsEnvironment.Deactivate(pluginId)
   101  				if deactivated && plugin.Manifest.HasClient() {
   102  					message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PLUGIN_DISABLED, "", "", "", nil)
   103  					message.Add("manifest", plugin.Manifest.ClientManifest())
   104  					a.Publish(message)
   105  				}
   106  			}
   107  		}
   108  
   109  		// Activate any plugins that have been enabled
   110  		for _, plugin := range availablePlugins {
   111  			if plugin.Manifest == nil {
   112  				plugin.WrapLogger(a.Log()).Error("Plugin manifest could not be loaded", mlog.Err(plugin.ManifestError))
   113  				continue
   114  			}
   115  
   116  			// Determine if plugin is enabled
   117  			pluginId := plugin.Manifest.Id
   118  			pluginEnabled := false
   119  			if state, ok := config.PluginStates[pluginId]; ok {
   120  				pluginEnabled = state.Enable
   121  			}
   122  
   123  			// Activate plugin if enabled
   124  			if pluginEnabled {
   125  				updatedManifest, activated, err := pluginsEnvironment.Activate(pluginId)
   126  				if err != nil {
   127  					plugin.WrapLogger(a.Log()).Error("Unable to activate plugin", mlog.Err(err))
   128  					continue
   129  				}
   130  
   131  				if activated {
   132  					// Notify all cluster clients if ready
   133  					if err := a.notifyPluginEnabled(updatedManifest); err != nil {
   134  						a.Log().Error("Failed to notify cluster on plugin enable", mlog.Err(err))
   135  					}
   136  				}
   137  			}
   138  		}
   139  	} else { // If plugins are disabled, shutdown plugins.
   140  		pluginsEnvironment.Shutdown()
   141  	}
   142  
   143  	if err := a.notifyPluginStatusesChanged(); err != nil {
   144  		mlog.Error("failed to notify plugin status changed", mlog.Err(err))
   145  	}
   146  }
   147  
   148  func (a *App) NewPluginAPI(manifest *model.Manifest) plugin.API {
   149  	return NewPluginAPI(a, manifest)
   150  }
   151  
   152  func (a *App) InitPlugins(pluginDir, webappPluginDir string) {
   153  	// Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
   154  	a.Srv().PluginsLock.RLock()
   155  	pluginsEnvironment := a.Srv().PluginsEnvironment
   156  	a.Srv().PluginsLock.RUnlock()
   157  	if pluginsEnvironment != nil || !*a.Config().PluginSettings.Enable {
   158  		a.SyncPluginsActiveState()
   159  		return
   160  	}
   161  
   162  	a.Log().Info("Starting up plugins")
   163  
   164  	if err := os.Mkdir(pluginDir, 0744); err != nil && !os.IsExist(err) {
   165  		mlog.Error("Failed to start up plugins", mlog.Err(err))
   166  		return
   167  	}
   168  
   169  	if err := os.Mkdir(webappPluginDir, 0744); err != nil && !os.IsExist(err) {
   170  		mlog.Error("Failed to start up plugins", mlog.Err(err))
   171  		return
   172  	}
   173  
   174  	env, err := plugin.NewEnvironment(a.NewPluginAPI, pluginDir, webappPluginDir, a.Log(), a.Metrics())
   175  	if err != nil {
   176  		mlog.Error("Failed to start up plugins", mlog.Err(err))
   177  		return
   178  	}
   179  	a.SetPluginsEnvironment(env)
   180  
   181  	if err := a.SyncPlugins(); err != nil {
   182  		mlog.Error("Failed to sync plugins from the file store", mlog.Err(err))
   183  	}
   184  
   185  	plugins := a.processPrepackagedPlugins(prepackagedPluginsDir)
   186  	pluginsEnvironment = a.GetPluginsEnvironment()
   187  	if pluginsEnvironment == nil {
   188  		mlog.Info("Plugins environment not found, server is likely shutting down")
   189  		return
   190  	}
   191  	pluginsEnvironment.SetPrepackagedPlugins(plugins)
   192  
   193  	// Sync plugin active state when config changes. Also notify plugins.
   194  	a.Srv().PluginsLock.Lock()
   195  	a.RemoveConfigListener(a.Srv().PluginConfigListenerId)
   196  	a.Srv().PluginConfigListenerId = a.AddConfigListener(func(*model.Config, *model.Config) {
   197  		a.SyncPluginsActiveState()
   198  		if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
   199  			pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
   200  				if err := hooks.OnConfigurationChange(); err != nil {
   201  					a.Log().Error("Plugin OnConfigurationChange hook failed", mlog.Err(err))
   202  				}
   203  				return true
   204  			}, plugin.OnConfigurationChangeId)
   205  		}
   206  	})
   207  	a.Srv().PluginsLock.Unlock()
   208  
   209  	a.SyncPluginsActiveState()
   210  }
   211  
   212  // SyncPlugins synchronizes the plugins installed locally
   213  // with the plugin bundles available in the file store.
   214  func (a *App) SyncPlugins() *model.AppError {
   215  	mlog.Info("Syncing plugins from the file store")
   216  
   217  	pluginsEnvironment := a.GetPluginsEnvironment()
   218  	if pluginsEnvironment == nil {
   219  		return model.NewAppError("SyncPlugins", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
   220  	}
   221  
   222  	availablePlugins, err := pluginsEnvironment.Available()
   223  	if err != nil {
   224  		return model.NewAppError("SyncPlugins", "app.plugin.sync.read_local_folder.app_error", nil, err.Error(), http.StatusInternalServerError)
   225  	}
   226  
   227  	var wg sync.WaitGroup
   228  	for _, plugin := range availablePlugins {
   229  		wg.Add(1)
   230  		go func(pluginID string) {
   231  			defer wg.Done()
   232  			// Only handle managed plugins with .filestore flag file.
   233  			_, err := os.Stat(filepath.Join(*a.Config().PluginSettings.Directory, pluginID, managedPluginFileName))
   234  			if os.IsNotExist(err) {
   235  				mlog.Warn("Skipping sync for unmanaged plugin", mlog.String("plugin_id", pluginID))
   236  			} else if err != nil {
   237  				mlog.Error("Skipping sync for plugin after failure to check if managed", mlog.String("plugin_id", pluginID), mlog.Err(err))
   238  			} else {
   239  				mlog.Debug("Removing local installation of managed plugin before sync", mlog.String("plugin_id", pluginID))
   240  				if err := a.removePluginLocally(pluginID); err != nil {
   241  					mlog.Error("Failed to remove local installation of managed plugin before sync", mlog.String("plugin_id", pluginID), mlog.Err(err))
   242  				}
   243  			}
   244  		}(plugin.Manifest.Id)
   245  	}
   246  	wg.Wait()
   247  
   248  	// Install plugins from the file store.
   249  	pluginSignaturePathMap, appErr := a.getPluginsFromFolder()
   250  	if appErr != nil {
   251  		return appErr
   252  	}
   253  
   254  	for _, plugin := range pluginSignaturePathMap {
   255  		wg.Add(1)
   256  		go func(plugin *pluginSignaturePath) {
   257  			defer wg.Done()
   258  			reader, appErr := a.FileReader(plugin.path)
   259  			if appErr != nil {
   260  				mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(appErr))
   261  				return
   262  			}
   263  			defer reader.Close()
   264  
   265  			var signature filesstore.ReadCloseSeeker
   266  			if *a.Config().PluginSettings.RequirePluginSignature {
   267  				signature, appErr = a.FileReader(plugin.signaturePath)
   268  				if appErr != nil {
   269  					mlog.Error("Failed to open plugin signature from file store.", mlog.Err(appErr))
   270  					return
   271  				}
   272  				defer signature.Close()
   273  			}
   274  
   275  			mlog.Info("Syncing plugin from file store", mlog.String("bundle", plugin.path))
   276  			if _, err := a.installPluginLocally(reader, signature, installPluginLocallyAlways); err != nil {
   277  				mlog.Error("Failed to sync plugin from file store", mlog.String("bundle", plugin.path), mlog.Err(err))
   278  			}
   279  		}(plugin)
   280  	}
   281  
   282  	wg.Wait()
   283  	return nil
   284  }
   285  
   286  func (s *Server) ShutDownPlugins() {
   287  	pluginsEnvironment := s.GetPluginsEnvironment()
   288  	if pluginsEnvironment == nil {
   289  		return
   290  	}
   291  
   292  	mlog.Info("Shutting down plugins")
   293  
   294  	pluginsEnvironment.Shutdown()
   295  
   296  	s.RemoveConfigListener(s.PluginConfigListenerId)
   297  	s.PluginConfigListenerId = ""
   298  
   299  	// Acquiring lock manually before cleaning up PluginsEnvironment.
   300  	s.PluginsLock.Lock()
   301  	defer s.PluginsLock.Unlock()
   302  	if s.PluginsEnvironment == pluginsEnvironment {
   303  		s.PluginsEnvironment = nil
   304  	} else {
   305  		mlog.Warn("Another PluginsEnvironment detected while shutting down plugins.")
   306  	}
   307  }
   308  
   309  func (a *App) GetActivePluginManifests() ([]*model.Manifest, *model.AppError) {
   310  	pluginsEnvironment := a.GetPluginsEnvironment()
   311  	if pluginsEnvironment == nil {
   312  		return nil, model.NewAppError("GetActivePluginManifests", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
   313  	}
   314  
   315  	plugins := pluginsEnvironment.Active()
   316  
   317  	manifests := make([]*model.Manifest, len(plugins))
   318  	for i, plugin := range plugins {
   319  		manifests[i] = plugin.Manifest
   320  	}
   321  
   322  	return manifests, nil
   323  }
   324  
   325  // EnablePlugin will set the config for an installed plugin to enabled, triggering asynchronous
   326  // activation if inactive anywhere in the cluster.
   327  // Notifies cluster peers through config change.
   328  func (a *App) EnablePlugin(id string) *model.AppError {
   329  	pluginsEnvironment := a.GetPluginsEnvironment()
   330  	if pluginsEnvironment == nil {
   331  		return model.NewAppError("EnablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
   332  	}
   333  
   334  	availablePlugins, err := pluginsEnvironment.Available()
   335  	if err != nil {
   336  		return model.NewAppError("EnablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError)
   337  	}
   338  
   339  	id = strings.ToLower(id)
   340  
   341  	var manifest *model.Manifest
   342  	for _, p := range availablePlugins {
   343  		if p.Manifest.Id == id {
   344  			manifest = p.Manifest
   345  			break
   346  		}
   347  	}
   348  
   349  	if manifest == nil {
   350  		return model.NewAppError("EnablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusNotFound)
   351  	}
   352  
   353  	a.UpdateConfig(func(cfg *model.Config) {
   354  		cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: true}
   355  	})
   356  
   357  	// This call will implicitly invoke SyncPluginsActiveState which will activate enabled plugins.
   358  	if err := a.SaveConfig(a.Config(), true); err != nil {
   359  		if err.Id == "ent.cluster.save_config.error" {
   360  			return model.NewAppError("EnablePlugin", "app.plugin.cluster.save_config.app_error", nil, "", http.StatusInternalServerError)
   361  		}
   362  		return model.NewAppError("EnablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError)
   363  	}
   364  
   365  	return nil
   366  }
   367  
   368  // DisablePlugin will set the config for an installed plugin to disabled, triggering deactivation if active.
   369  // Notifies cluster peers through config change.
   370  func (a *App) DisablePlugin(id string) *model.AppError {
   371  	pluginsEnvironment := a.GetPluginsEnvironment()
   372  	if pluginsEnvironment == nil {
   373  		return model.NewAppError("DisablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
   374  	}
   375  
   376  	availablePlugins, err := pluginsEnvironment.Available()
   377  	if err != nil {
   378  		return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError)
   379  	}
   380  
   381  	id = strings.ToLower(id)
   382  
   383  	var manifest *model.Manifest
   384  	for _, p := range availablePlugins {
   385  		if p.Manifest.Id == id {
   386  			manifest = p.Manifest
   387  			break
   388  		}
   389  	}
   390  
   391  	if manifest == nil {
   392  		return model.NewAppError("DisablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusNotFound)
   393  	}
   394  
   395  	a.UpdateConfig(func(cfg *model.Config) {
   396  		cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: false}
   397  	})
   398  	a.UnregisterPluginCommands(id)
   399  
   400  	// This call will implicitly invoke SyncPluginsActiveState which will deactivate disabled plugins.
   401  	if err := a.SaveConfig(a.Config(), true); err != nil {
   402  		return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError)
   403  	}
   404  
   405  	return nil
   406  }
   407  
   408  func (a *App) GetPlugins() (*model.PluginsResponse, *model.AppError) {
   409  	pluginsEnvironment := a.GetPluginsEnvironment()
   410  	if pluginsEnvironment == nil {
   411  		return nil, model.NewAppError("GetPlugins", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
   412  	}
   413  
   414  	availablePlugins, err := pluginsEnvironment.Available()
   415  	if err != nil {
   416  		return nil, model.NewAppError("GetPlugins", "app.plugin.get_plugins.app_error", nil, err.Error(), http.StatusInternalServerError)
   417  	}
   418  	resp := &model.PluginsResponse{Active: []*model.PluginInfo{}, Inactive: []*model.PluginInfo{}}
   419  	for _, plugin := range availablePlugins {
   420  		if plugin.Manifest == nil {
   421  			continue
   422  		}
   423  
   424  		info := &model.PluginInfo{
   425  			Manifest: *plugin.Manifest,
   426  		}
   427  
   428  		if pluginsEnvironment.IsActive(plugin.Manifest.Id) {
   429  			resp.Active = append(resp.Active, info)
   430  		} else {
   431  			resp.Inactive = append(resp.Inactive, info)
   432  		}
   433  	}
   434  
   435  	return resp, nil
   436  }
   437  
   438  // GetMarketplacePlugins returns a list of plugins from the marketplace-server,
   439  // and plugins that are installed locally.
   440  func (a *App) GetMarketplacePlugins(filter *model.MarketplacePluginFilter) ([]*model.MarketplacePlugin, *model.AppError) {
   441  	plugins := map[string]*model.MarketplacePlugin{}
   442  
   443  	if *a.Config().PluginSettings.EnableRemoteMarketplace && !filter.LocalOnly {
   444  		p, appErr := a.getRemotePlugins()
   445  		if appErr != nil {
   446  			return nil, appErr
   447  		}
   448  		plugins = p
   449  	}
   450  
   451  	appErr := a.mergePrepackagedPlugins(plugins)
   452  	if appErr != nil {
   453  		return nil, appErr
   454  	}
   455  
   456  	appErr = a.mergeLocalPlugins(plugins)
   457  	if appErr != nil {
   458  		return nil, appErr
   459  	}
   460  
   461  	// Filter plugins.
   462  	var result []*model.MarketplacePlugin
   463  	for _, p := range plugins {
   464  		if pluginMatchesFilter(p.Manifest, filter.Filter) {
   465  			result = append(result, p)
   466  		}
   467  	}
   468  
   469  	// Sort result alphabetically.
   470  	sort.SliceStable(result, func(i, j int) bool {
   471  		return strings.ToLower(result[i].Manifest.Name) < strings.ToLower(result[j].Manifest.Name)
   472  	})
   473  
   474  	return result, nil
   475  }
   476  
   477  // getPrepackagedPlugin returns a pre-packaged plugin.
   478  func (a *App) getPrepackagedPlugin(pluginId, version string) (*plugin.PrepackagedPlugin, *model.AppError) {
   479  	pluginsEnvironment := a.GetPluginsEnvironment()
   480  	if pluginsEnvironment == nil {
   481  		return nil, model.NewAppError("getPrepackagedPlugin", "app.plugin.config.app_error", nil, "plugin environment is nil", http.StatusInternalServerError)
   482  	}
   483  
   484  	prepackagedPlugins := pluginsEnvironment.PrepackagedPlugins()
   485  	for _, p := range prepackagedPlugins {
   486  		if p.Manifest.Id == pluginId && p.Manifest.Version == version {
   487  			return p, nil
   488  		}
   489  	}
   490  
   491  	return nil, model.NewAppError("getPrepackagedPlugin", "app.plugin.marketplace_plugins.not_found.app_error", nil, "", http.StatusInternalServerError)
   492  }
   493  
   494  // getRemoteMarketplacePlugin returns plugin from marketplace-server.
   495  func (a *App) getRemoteMarketplacePlugin(pluginId, version string) (*model.BaseMarketplacePlugin, *model.AppError) {
   496  	marketplaceClient, err := marketplace.NewClient(
   497  		*a.Config().PluginSettings.MarketplaceUrl,
   498  		a.HTTPService(),
   499  	)
   500  	if err != nil {
   501  		return nil, model.NewAppError("GetMarketplacePlugin", "app.plugin.marketplace_client.app_error", nil, err.Error(), http.StatusInternalServerError)
   502  	}
   503  
   504  	filter := a.getBaseMarketplaceFilter()
   505  	filter.Filter = pluginId
   506  
   507  	plugin, err := marketplaceClient.GetPlugin(filter, version)
   508  	if err != nil {
   509  		return nil, model.NewAppError("GetMarketplacePlugin", "app.plugin.marketplace_plugins.not_found.app_error", nil, err.Error(), http.StatusInternalServerError)
   510  	}
   511  
   512  	return plugin, nil
   513  }
   514  
   515  func (a *App) getRemotePlugins() (map[string]*model.MarketplacePlugin, *model.AppError) {
   516  	result := map[string]*model.MarketplacePlugin{}
   517  
   518  	pluginsEnvironment := a.GetPluginsEnvironment()
   519  	if pluginsEnvironment == nil {
   520  		return nil, model.NewAppError("getRemotePlugins", "app.plugin.config.app_error", nil, "", http.StatusInternalServerError)
   521  	}
   522  
   523  	marketplaceClient, err := marketplace.NewClient(
   524  		*a.Config().PluginSettings.MarketplaceUrl,
   525  		a.HTTPService(),
   526  	)
   527  	if err != nil {
   528  		return nil, model.NewAppError("getRemotePlugins", "app.plugin.marketplace_client.app_error", nil, err.Error(), http.StatusInternalServerError)
   529  	}
   530  
   531  	filter := a.getBaseMarketplaceFilter()
   532  	// Fetch all plugins from marketplace.
   533  	filter.PerPage = -1
   534  
   535  	marketplacePlugins, err := marketplaceClient.GetPlugins(filter)
   536  	if err != nil {
   537  		return nil, model.NewAppError("getRemotePlugins", "app.plugin.marketplace_client.failed_to_fetch", nil, err.Error(), http.StatusInternalServerError)
   538  	}
   539  
   540  	for _, p := range marketplacePlugins {
   541  		if p.Manifest == nil {
   542  			continue
   543  		}
   544  
   545  		result[p.Manifest.Id] = &model.MarketplacePlugin{BaseMarketplacePlugin: p}
   546  	}
   547  
   548  	return result, nil
   549  }
   550  
   551  // mergePrepackagedPlugins merges pre-packaged plugins to remote marketplace plugins list.
   552  func (a *App) mergePrepackagedPlugins(remoteMarketplacePlugins map[string]*model.MarketplacePlugin) *model.AppError {
   553  	pluginsEnvironment := a.GetPluginsEnvironment()
   554  	if pluginsEnvironment == nil {
   555  		return model.NewAppError("mergePrepackagedPlugins", "app.plugin.config.app_error", nil, "", http.StatusInternalServerError)
   556  	}
   557  
   558  	for _, prepackaged := range pluginsEnvironment.PrepackagedPlugins() {
   559  		if prepackaged.Manifest == nil {
   560  			continue
   561  		}
   562  
   563  		prepackagedMarketplace := &model.MarketplacePlugin{
   564  			BaseMarketplacePlugin: &model.BaseMarketplacePlugin{
   565  				HomepageURL:     prepackaged.Manifest.HomepageURL,
   566  				IconData:        prepackaged.IconData,
   567  				ReleaseNotesURL: prepackaged.Manifest.ReleaseNotesURL,
   568  				Manifest:        prepackaged.Manifest,
   569  			},
   570  		}
   571  
   572  		// If not available in marketplace, add the prepackaged
   573  		if remoteMarketplacePlugins[prepackaged.Manifest.Id] == nil {
   574  			remoteMarketplacePlugins[prepackaged.Manifest.Id] = prepackagedMarketplace
   575  			continue
   576  		}
   577  
   578  		// If available in the markteplace, only overwrite if newer.
   579  		prepackagedVersion, err := semver.Parse(prepackaged.Manifest.Version)
   580  		if err != nil {
   581  			return model.NewAppError("mergePrepackagedPlugins", "app.plugin.invalid_version.app_error", nil, "", http.StatusBadRequest)
   582  		}
   583  
   584  		marketplacePlugin := remoteMarketplacePlugins[prepackaged.Manifest.Id]
   585  		marketplaceVersion, err := semver.Parse(marketplacePlugin.Manifest.Version)
   586  		if err != nil {
   587  			return model.NewAppError("mergePrepackagedPlugins", "app.plugin.invalid_version.app_error", nil, "", http.StatusBadRequest)
   588  		}
   589  
   590  		if prepackagedVersion.GT(marketplaceVersion) {
   591  			remoteMarketplacePlugins[prepackaged.Manifest.Id] = prepackagedMarketplace
   592  		}
   593  	}
   594  
   595  	return nil
   596  }
   597  
   598  // mergeLocalPlugins merges locally installed plugins to remote marketplace plugins list.
   599  func (a *App) mergeLocalPlugins(remoteMarketplacePlugins map[string]*model.MarketplacePlugin) *model.AppError {
   600  	pluginsEnvironment := a.GetPluginsEnvironment()
   601  	if pluginsEnvironment == nil {
   602  		return model.NewAppError("GetMarketplacePlugins", "app.plugin.config.app_error", nil, "", http.StatusInternalServerError)
   603  	}
   604  
   605  	localPlugins, err := pluginsEnvironment.Available()
   606  	if err != nil {
   607  		return model.NewAppError("GetMarketplacePlugins", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError)
   608  	}
   609  
   610  	for _, plugin := range localPlugins {
   611  		if plugin.Manifest == nil {
   612  			continue
   613  		}
   614  
   615  		if remoteMarketplacePlugins[plugin.Manifest.Id] != nil {
   616  			// Remote plugin is installed.
   617  			remoteMarketplacePlugins[plugin.Manifest.Id].InstalledVersion = plugin.Manifest.Version
   618  			continue
   619  		}
   620  
   621  		iconData := ""
   622  		if plugin.Manifest.IconPath != "" {
   623  			iconData, err = getIcon(filepath.Join(plugin.Path, plugin.Manifest.IconPath))
   624  			if err != nil {
   625  				mlog.Warn("Error loading local plugin icon", mlog.String("plugin", plugin.Manifest.Id), mlog.String("icon_path", plugin.Manifest.IconPath), mlog.Err(err))
   626  			}
   627  		}
   628  
   629  		var labels []model.MarketplaceLabel
   630  		if *a.Config().PluginSettings.EnableRemoteMarketplace {
   631  			// Labels should not (yet) be localized as the labels sent by the Marketplace are not (yet) localizable.
   632  			labels = append(labels, model.MarketplaceLabel{
   633  				Name:        "Local",
   634  				Description: "This plugin is not listed in the marketplace",
   635  			})
   636  		}
   637  
   638  		remoteMarketplacePlugins[plugin.Manifest.Id] = &model.MarketplacePlugin{
   639  			BaseMarketplacePlugin: &model.BaseMarketplacePlugin{
   640  				HomepageURL:     plugin.Manifest.HomepageURL,
   641  				IconData:        iconData,
   642  				ReleaseNotesURL: plugin.Manifest.ReleaseNotesURL,
   643  				Labels:          labels,
   644  				Manifest:        plugin.Manifest,
   645  			},
   646  			InstalledVersion: plugin.Manifest.Version,
   647  		}
   648  	}
   649  
   650  	return nil
   651  }
   652  
   653  func (a *App) getBaseMarketplaceFilter() *model.MarketplacePluginFilter {
   654  	filter := &model.MarketplacePluginFilter{
   655  		ServerVersion: model.CurrentVersion,
   656  	}
   657  
   658  	license := a.Srv().License()
   659  	if license != nil && *license.Features.EnterprisePlugins {
   660  		filter.EnterprisePlugins = true
   661  	}
   662  
   663  	if model.BuildEnterpriseReady == "true" {
   664  		filter.BuildEnterpriseReady = true
   665  	}
   666  
   667  	return filter
   668  }
   669  
   670  func pluginMatchesFilter(manifest *model.Manifest, filter string) bool {
   671  	filter = strings.TrimSpace(strings.ToLower(filter))
   672  
   673  	if filter == "" {
   674  		return true
   675  	}
   676  
   677  	if strings.ToLower(manifest.Id) == filter {
   678  		return true
   679  	}
   680  
   681  	if strings.Contains(strings.ToLower(manifest.Name), filter) {
   682  		return true
   683  	}
   684  
   685  	if strings.Contains(strings.ToLower(manifest.Description), filter) {
   686  		return true
   687  	}
   688  
   689  	return false
   690  }
   691  
   692  // notifyPluginEnabled notifies connected websocket clients across all peers if the version of the given
   693  // plugin is same across them.
   694  //
   695  // When a peer finds itself in agreement with all other peers as to the version of the given plugin,
   696  // it will notify all connected websocket clients (across all peers) to trigger the (re-)installation.
   697  // There is a small chance that this never occurs, because the last server to finish installing dies before it can announce.
   698  // There is also a chance that multiple servers notify, but the webapp handles this idempotently.
   699  func (a *App) notifyPluginEnabled(manifest *model.Manifest) error {
   700  	pluginsEnvironment := a.GetPluginsEnvironment()
   701  	if pluginsEnvironment == nil {
   702  		return errors.New("pluginsEnvironment is nil")
   703  	}
   704  	if !manifest.HasClient() || !pluginsEnvironment.IsActive(manifest.Id) {
   705  		return nil
   706  	}
   707  
   708  	var statuses model.PluginStatuses
   709  
   710  	if a.Cluster() != nil {
   711  		var err *model.AppError
   712  		statuses, err = a.Cluster().GetPluginStatuses()
   713  		if err != nil {
   714  			return err
   715  		}
   716  	}
   717  
   718  	localStatus, err := a.GetPluginStatus(manifest.Id)
   719  	if err != nil {
   720  		return err
   721  	}
   722  	statuses = append(statuses, localStatus)
   723  
   724  	// This will not guard against the race condition of enabling a plugin immediately after installation.
   725  	// As GetPluginStatuses() will not return the new plugin (since other peers are racing to install),
   726  	// this peer will end up checking status against itself and will notify all webclients (including peer webclients),
   727  	// which may result in a 404.
   728  	for _, status := range statuses {
   729  		if status.PluginId == manifest.Id && status.Version != manifest.Version {
   730  			mlog.Debug("Not ready to notify webclients", mlog.String("cluster_id", status.ClusterId), mlog.String("plugin_id", manifest.Id))
   731  			return nil
   732  		}
   733  	}
   734  
   735  	// Notify all cluster peer clients.
   736  	message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PLUGIN_ENABLED, "", "", "", nil)
   737  	message.Add("manifest", manifest.ClientManifest())
   738  	a.Publish(message)
   739  
   740  	return nil
   741  }
   742  
   743  func (a *App) getPluginsFromFolder() (map[string]*pluginSignaturePath, *model.AppError) {
   744  	fileStorePaths, appErr := a.ListDirectory(fileStorePluginFolder)
   745  	if appErr != nil {
   746  		return nil, model.NewAppError("getPluginsFromDir", "app.plugin.sync.list_filestore.app_error", nil, appErr.Error(), http.StatusInternalServerError)
   747  	}
   748  
   749  	return getPluginsFromFilePaths(fileStorePaths), nil
   750  }
   751  
   752  func getPluginsFromFilePaths(fileStorePaths []string) map[string]*pluginSignaturePath {
   753  	pluginSignaturePathMap := make(map[string]*pluginSignaturePath)
   754  	for _, path := range fileStorePaths {
   755  		if strings.HasSuffix(path, ".tar.gz") {
   756  			id := strings.TrimSuffix(filepath.Base(path), ".tar.gz")
   757  			helper := &pluginSignaturePath{
   758  				pluginId:      id,
   759  				path:          path,
   760  				signaturePath: "",
   761  			}
   762  			pluginSignaturePathMap[id] = helper
   763  		}
   764  	}
   765  	for _, path := range fileStorePaths {
   766  		if strings.HasSuffix(path, ".tar.gz.sig") {
   767  			id := strings.TrimSuffix(filepath.Base(path), ".tar.gz.sig")
   768  			if val, ok := pluginSignaturePathMap[id]; !ok {
   769  				mlog.Error("Unknown signature", mlog.String("path", path))
   770  			} else {
   771  				val.signaturePath = path
   772  			}
   773  		}
   774  	}
   775  
   776  	return pluginSignaturePathMap
   777  }
   778  
   779  func (a *App) processPrepackagedPlugins(pluginsDir string) []*plugin.PrepackagedPlugin {
   780  	prepackagedPluginsDir, found := fileutils.FindDir(pluginsDir)
   781  	if !found {
   782  		return nil
   783  	}
   784  
   785  	var fileStorePaths []string
   786  	err := filepath.Walk(prepackagedPluginsDir, func(walkPath string, info os.FileInfo, err error) error {
   787  		fileStorePaths = append(fileStorePaths, walkPath)
   788  		return nil
   789  	})
   790  	if err != nil {
   791  		mlog.Error("Failed to walk prepackaged plugins", mlog.Err(err))
   792  		return nil
   793  	}
   794  
   795  	pluginSignaturePathMap := getPluginsFromFilePaths(fileStorePaths)
   796  	plugins := make([]*plugin.PrepackagedPlugin, 0, len(pluginSignaturePathMap))
   797  	prepackagedPlugins := make(chan *plugin.PrepackagedPlugin, len(pluginSignaturePathMap))
   798  
   799  	var wg sync.WaitGroup
   800  	for _, psPath := range pluginSignaturePathMap {
   801  		wg.Add(1)
   802  		go func(psPath *pluginSignaturePath) {
   803  			defer wg.Done()
   804  			p, err := a.processPrepackagedPlugin(psPath)
   805  			if err != nil {
   806  				mlog.Error("Failed to install prepackaged plugin", mlog.String("path", psPath.path), mlog.Err(err))
   807  				return
   808  			}
   809  			prepackagedPlugins <- p
   810  		}(psPath)
   811  	}
   812  
   813  	wg.Wait()
   814  	close(prepackagedPlugins)
   815  
   816  	for p := range prepackagedPlugins {
   817  		plugins = append(plugins, p)
   818  	}
   819  
   820  	return plugins
   821  }
   822  
   823  // processPrepackagedPlugin will return the prepackaged plugin metadata and will also
   824  // install the prepackaged plugin if it had been previously enabled and AutomaticPrepackagedPlugins is true.
   825  func (a *App) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*plugin.PrepackagedPlugin, error) {
   826  	mlog.Debug("Processing prepackaged plugin", mlog.String("path", pluginPath.path))
   827  
   828  	fileReader, err := os.Open(pluginPath.path)
   829  	if err != nil {
   830  		return nil, errors.Wrapf(err, "Failed to open prepackaged plugin %s", pluginPath.path)
   831  	}
   832  	defer fileReader.Close()
   833  
   834  	tmpDir, err := ioutil.TempDir("", "plugintmp")
   835  	if err != nil {
   836  		return nil, errors.Wrap(err, "Failed to create temp dir plugintmp")
   837  	}
   838  	defer os.RemoveAll(tmpDir)
   839  
   840  	plugin, pluginDir, err := getPrepackagedPlugin(pluginPath, fileReader, tmpDir)
   841  	if err != nil {
   842  		return nil, errors.Wrapf(err, "Failed to get prepackaged plugin %s", pluginPath.path)
   843  	}
   844  
   845  	// Skip installing the plugin at all if automatic prepackaged plugins is disabled
   846  	if !*a.Config().PluginSettings.AutomaticPrepackagedPlugins {
   847  		return plugin, nil
   848  	}
   849  
   850  	// Skip installing if the plugin is has not been previously enabled.
   851  	pluginState := a.Config().PluginSettings.PluginStates[plugin.Manifest.Id]
   852  	if pluginState == nil || !pluginState.Enable {
   853  		return plugin, nil
   854  	}
   855  
   856  	mlog.Debug("Installing prepackaged plugin", mlog.String("path", pluginPath.path))
   857  	if _, err := a.installExtractedPlugin(plugin.Manifest, pluginDir, installPluginLocallyOnlyIfNewOrUpgrade); err != nil {
   858  		return nil, errors.Wrapf(err, "Failed to install extracted prepackaged plugin %s", pluginPath.path)
   859  	}
   860  
   861  	return plugin, nil
   862  }
   863  
   864  // getPrepackagedPlugin builds a PrepackagedPlugin from the plugin at the given path, additionally returning the directory in which it was extracted.
   865  func getPrepackagedPlugin(pluginPath *pluginSignaturePath, pluginFile io.ReadSeeker, tmpDir string) (*plugin.PrepackagedPlugin, string, error) {
   866  	manifest, pluginDir, appErr := extractPlugin(pluginFile, tmpDir)
   867  	if appErr != nil {
   868  		return nil, "", errors.Wrapf(appErr, "Failed to extract plugin with path %s", pluginPath.path)
   869  	}
   870  
   871  	plugin := new(plugin.PrepackagedPlugin)
   872  	plugin.Manifest = manifest
   873  	plugin.Path = pluginPath.path
   874  
   875  	if pluginPath.signaturePath != "" {
   876  		sig := pluginPath.signaturePath
   877  		sigReader, sigErr := os.Open(sig)
   878  		if sigErr != nil {
   879  			return nil, "", errors.Wrapf(sigErr, "Failed to open prepackaged plugin signature %s", sig)
   880  		}
   881  		bytes, sigErr := ioutil.ReadAll(sigReader)
   882  		if sigErr != nil {
   883  			return nil, "", errors.Wrapf(sigErr, "Failed to read prepackaged plugin signature %s", sig)
   884  		}
   885  		plugin.Signature = bytes
   886  	}
   887  
   888  	if manifest.IconPath != "" {
   889  		iconData, err := getIcon(filepath.Join(pluginDir, manifest.IconPath))
   890  		if err != nil {
   891  			return nil, "", errors.Wrapf(err, "Failed to read icon at %s", manifest.IconPath)
   892  		}
   893  		plugin.IconData = iconData
   894  	}
   895  
   896  	return plugin, pluginDir, nil
   897  }
   898  
   899  func getIcon(iconPath string) (string, error) {
   900  	icon, err := ioutil.ReadFile(iconPath)
   901  	if err != nil {
   902  		return "", errors.Wrapf(err, "failed to open icon at path %s", iconPath)
   903  	}
   904  
   905  	if !svg.Is(icon) {
   906  		return "", errors.Errorf("icon is not svg %s", iconPath)
   907  	}
   908  
   909  	return fmt.Sprintf("data:image/svg+xml;base64,%s", base64.StdEncoding.EncodeToString(icon)), nil
   910  }