diff --git a/api4/plugin.go b/api4/plugin.go index be5b298d021..f0045b4ecb4 100644 --- a/api4/plugin.go +++ b/api4/plugin.go @@ -288,17 +288,18 @@ func getMarketplacePlugins(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadPlugins) { - c.SetPermissionError(model.PermissionSysconsoleReadPlugins) - return - } - filter, err := parseMarketplacePluginFilter(r.URL) if err != nil { c.Err = model.NewAppError("getMarketplacePlugins", "app.plugin.marshal.app_error", nil, "", http.StatusInternalServerError).Wrap(err) return } + // if we are looking for remote only, we don't need to check for permissions + if !filter.RemoteOnly && !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadPlugins) { + c.SetPermissionError(model.PermissionSysconsoleReadPlugins) + return + } + plugins, appErr := c.App.GetMarketplacePlugins(filter) if appErr != nil { c.Err = appErr @@ -386,12 +387,19 @@ func parseMarketplacePluginFilter(u *url.URL) (*model.MarketplacePluginFilter, e filter := u.Query().Get("filter") serverVersion := u.Query().Get("server_version") localOnly, _ := strconv.ParseBool(u.Query().Get("local_only")) + remoteOnly, _ := strconv.ParseBool(u.Query().Get("remote_only")) + + if localOnly && remoteOnly { + return nil, errors.New("local_only and remote_only cannot be both true") + } + return &model.MarketplacePluginFilter{ Page: page, PerPage: perPage, Filter: filter, ServerVersion: serverVersion, LocalOnly: localOnly, + RemoteOnly: remoteOnly, }, nil } diff --git a/api4/plugin_test.go b/api4/plugin_test.go index 1967f9a6171..437649be2bf 100644 --- a/api4/plugin_test.go +++ b/api4/plugin_test.go @@ -1126,6 +1126,62 @@ func TestGetLocalPluginInMarketplace(t *testing.T) { }) } +func TestGetRemotePluginInMarketplace(t *testing.T) { + th := Setup(t) + defer th.TearDown() + + samplePlugins := []*model.MarketplacePlugin{ + { + BaseMarketplacePlugin: &model.BaseMarketplacePlugin{ + HomepageURL: "https://example.com/mattermost/mattermost-plugin-nps", + IconData: "https://example.com/icon.svg", + DownloadURL: "www.github.com/example", + Manifest: &model.Manifest{ + Id: "testplugin2", + Name: "testplugin2", + Description: "a second plugin", + Version: "1.2.2", + MinServerVersion: "", + }, + }, + InstalledVersion: "", + }, + } + + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + res.WriteHeader(http.StatusOK) + json, err := json.Marshal([]*model.MarketplacePlugin{samplePlugins[0]}) + require.NoError(t, err) + res.Write(json) + })) + defer testServer.Close() + + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.PluginSettings.Enable = true + *cfg.PluginSettings.EnableMarketplace = true + *cfg.PluginSettings.EnableRemoteMarketplace = true + *cfg.PluginSettings.EnableUploads = true + *cfg.PluginSettings.MarketplaceURL = testServer.URL + }) + + // Upload one local plugin + path, _ := fileutils.FindDir("tests") + tarData, err := os.ReadFile(filepath.Join(path, "testplugin.tar.gz")) + require.NoError(t, err) + + manifest, _, err := th.SystemAdminClient.UploadPlugin(bytes.NewReader(tarData)) + require.NoError(t, err) + + plugins, _, err := th.SystemAdminClient.GetMarketplacePlugins(&model.MarketplacePluginFilter{RemoteOnly: true}) + require.NoError(t, err) + + require.Len(t, plugins, 1) + require.Equal(t, samplePlugins[0], plugins[0]) + + _, err = th.SystemAdminClient.RemovePlugin(manifest.Id) + require.NoError(t, err) +} + func TestGetPrepackagedPluginInMarketplace(t *testing.T) { th := Setup(t) defer th.TearDown() diff --git a/app/plugin.go b/app/plugin.go index 1e367358940..5cf64ba8a16 100644 --- a/app/plugin.go +++ b/app/plugin.go @@ -571,24 +571,26 @@ func (a *App) GetMarketplacePlugins(filter *model.MarketplacePluginFilter) ([]*m plugins = p } - // Some plugin don't work on cloud. The remote Marketplace is aware of this fact, - // but prepackaged plugins are not. Hence, on a cloud installation prepackaged plugins - // shouldn't be shown in the Marketplace modal. - // This is a short term fix. The long term solution is to have a separate set of - // prepacked plugins for cloud: https://mattermost.atlassian.net/browse/MM-31331. - license := a.Srv().License() - if license == nil || !license.IsCloud() { - appErr := a.mergePrepackagedPlugins(plugins) + if !filter.RemoteOnly { + // Some plugin don't work on cloud. The remote Marketplace is aware of this fact, + // but prepackaged plugins are not. Hence, on a cloud installation prepackaged plugins + // shouldn't be shown in the Marketplace modal. + // This is a short term fix. The long term solution is to have a separate set of + // prepacked plugins for cloud: https://mattermost.atlassian.net/browse/MM-31331. + license := a.Srv().License() + if license == nil || !license.IsCloud() { + appErr := a.mergePrepackagedPlugins(plugins) + if appErr != nil { + return nil, appErr + } + } + + appErr := a.mergeLocalPlugins(plugins) if appErr != nil { return nil, appErr } } - appErr := a.mergeLocalPlugins(plugins) - if appErr != nil { - return nil, appErr - } - // Filter plugins. var result []*model.MarketplacePlugin for _, p := range plugins { diff --git a/model/marketplace_plugin.go b/model/marketplace_plugin.go index 8f0371b1177..1d19515d544 100644 --- a/model/marketplace_plugin.go +++ b/model/marketplace_plugin.go @@ -89,6 +89,7 @@ type MarketplacePluginFilter struct { Platform string PluginId string ReturnAllVersions bool + RemoteOnly bool } // ApplyToURL modifies the given url to include query string parameters for the request. @@ -104,6 +105,7 @@ func (filter *MarketplacePluginFilter) ApplyToURL(u *url.URL) { q.Add("enterprise_plugins", strconv.FormatBool(filter.EnterprisePlugins)) q.Add("cloud", strconv.FormatBool(filter.Cloud)) q.Add("local_only", strconv.FormatBool(filter.LocalOnly)) + q.Add("remote_only", strconv.FormatBool(filter.RemoteOnly)) q.Add("platform", filter.Platform) q.Add("plugin_id", filter.PluginId) q.Add("return_all_versions", strconv.FormatBool(filter.ReturnAllVersions))