From 899ab31fff9b34bc125faf75b79a89e390deb2cf Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Fri, 1 Sep 2017 09:00:27 -0400 Subject: Implement experimental REST API endpoints for plugins (#7279) * Implement experimental REST API endpoints for plugins * Updates per feedback and rebase * Update tests * Further updates * Update extraction of plugins * Use OS temp dir for plugins instead of search path * Fail extraction on paths that attempt to traverse upward * Update pluginenv ActivePlugins() --- app/plugins.go | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) (limited to 'app/plugins.go') diff --git a/app/plugins.go b/app/plugins.go index 82eda067c..51f6414a3 100644 --- a/app/plugins.go +++ b/app/plugins.go @@ -5,7 +5,12 @@ package app import ( "encoding/json" + "io" + "io/ioutil" "net/http" + "os" + "path/filepath" + "strings" l4g "github.com/alecthomas/log4go" @@ -84,3 +89,142 @@ func InitPlugins() { p.OnConfigurationChange() } } + +func ActivatePlugins() { + if Srv.PluginEnv == nil { + l4g.Error("plugin env not initialized") + return + } + + plugins, err := Srv.PluginEnv.Plugins() + if err != nil { + l4g.Error("failed to start up plugins: " + err.Error()) + return + } + + for _, plugin := range plugins { + err := Srv.PluginEnv.ActivatePlugin(plugin.Manifest.Id) + if err != nil { + l4g.Error(err.Error()) + } + l4g.Info("Activated %v plugin", plugin.Manifest.Id) + } +} + +func UnpackAndActivatePlugin(pluginFile io.Reader) (*model.Manifest, *model.AppError) { + if Srv.PluginEnv == nil || !*utils.Cfg.PluginSettings.Enable { + return nil, model.NewAppError("UnpackAndActivatePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) + } + + tmpDir, err := ioutil.TempDir("", "plugintmp") + if err != nil { + return nil, model.NewAppError("UnpackAndActivatePlugin", "app.plugin.temp_dir.app_error", nil, err.Error(), http.StatusInternalServerError) + } + defer func() { + os.RemoveAll(tmpDir) + }() + + filenames, err := utils.ExtractTarGz(pluginFile, tmpDir) + if err != nil { + return nil, model.NewAppError("UnpackAndActivatePlugin", "app.plugin.extract.app_error", nil, err.Error(), http.StatusBadRequest) + } + + if len(filenames) == 0 { + return nil, model.NewAppError("UnpackAndActivatePlugin", "app.plugin.no_files.app_error", nil, err.Error(), http.StatusBadRequest) + } + + splitPath := strings.Split(filenames[0], string(os.PathSeparator)) + + if len(splitPath) == 0 { + return nil, model.NewAppError("UnpackAndActivatePlugin", "app.plugin.bad_path.app_error", nil, err.Error(), http.StatusBadRequest) + } + + manifestDir := filepath.Join(tmpDir, splitPath[0]) + + manifest, _, err := model.FindManifest(manifestDir) + if err != nil { + return nil, model.NewAppError("UnpackAndActivatePlugin", "app.plugin.manifest.app_error", nil, err.Error(), http.StatusBadRequest) + } + + os.Rename(manifestDir, filepath.Join(Srv.PluginEnv.SearchPath(), manifest.Id)) + if err != nil { + return nil, model.NewAppError("UnpackAndActivatePlugin", "app.plugin.mvdir.app_error", nil, err.Error(), http.StatusInternalServerError) + } + + // Should add manifest validation and error handling here + + err = Srv.PluginEnv.ActivatePlugin(manifest.Id) + if err != nil { + return nil, model.NewAppError("UnpackAndActivatePlugin", "app.plugin.activate.app_error", nil, err.Error(), http.StatusBadRequest) + } + + return manifest, nil +} + +func GetActivePluginManifests() ([]*model.Manifest, *model.AppError) { + if Srv.PluginEnv == nil || !*utils.Cfg.PluginSettings.Enable { + return nil, model.NewAppError("GetActivePluginManifests", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) + } + + plugins, err := Srv.PluginEnv.ActivePlugins() + if err != nil { + return nil, model.NewAppError("GetActivePluginManifests", "app.plugin.get_plugins.app_error", nil, err.Error(), http.StatusInternalServerError) + } + + manifests := make([]*model.Manifest, len(plugins)) + for i, plugin := range plugins { + manifests[i] = plugin.Manifest + } + + return manifests, nil +} + +func RemovePlugin(id string) *model.AppError { + if Srv.PluginEnv == nil || !*utils.Cfg.PluginSettings.Enable { + return model.NewAppError("RemovePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) + } + + err := Srv.PluginEnv.DeactivatePlugin(id) + if err != nil { + return model.NewAppError("RemovePlugin", "app.plugin.deactivate.app_error", nil, err.Error(), http.StatusBadRequest) + } + + err = os.RemoveAll(filepath.Join(Srv.PluginEnv.SearchPath(), id)) + if err != nil { + return model.NewAppError("RemovePlugin", "app.plugin.remove.app_error", nil, err.Error(), http.StatusInternalServerError) + } + + return nil +} + +// Temporary WIP function/type for experimental webapp plugins +type ClientConfigPlugin struct { + Id string `json:"id"` + BundlePath string `json:"bundle_path"` +} + +func GetPluginsForClientConfig() string { + if Srv.PluginEnv == nil || !*utils.Cfg.PluginSettings.Enable { + return "" + } + + plugins, err := Srv.PluginEnv.ActivePlugins() + if err != nil { + return "" + } + + pluginsConfig := []ClientConfigPlugin{} + for _, plugin := range plugins { + if plugin.Manifest.Webapp == nil { + continue + } + pluginsConfig = append(pluginsConfig, ClientConfigPlugin{Id: plugin.Manifest.Id, BundlePath: plugin.Manifest.Webapp.BundlePath}) + } + + b, err := json.Marshal(pluginsConfig) + if err != nil { + return "" + } + + return string(b) +} -- cgit v1.2.3-1-g7c22