diff --git a/api4/plugin.go b/api4/plugin.go index 5163c9df07..0c7f7a773b 100644 --- a/api4/plugin.go +++ b/api4/plugin.go @@ -151,7 +151,7 @@ func installMarketplacePlugin(c *Context, w http.ResponseWriter, r *http.Request } auditRec.AddMeta("plugin_id", pluginRequest.Id) - manifest, appErr := c.App.InstallMarketplacePlugin(pluginRequest) + manifest, appErr := c.App.Channels().InstallMarketplacePlugin(pluginRequest) if appErr != nil { c.Err = appErr return @@ -231,7 +231,7 @@ func removePlugin(c *Context, w http.ResponseWriter, r *http.Request) { return } - err := c.App.RemovePlugin(c.Params.PluginId) + err := c.App.Channels().RemovePlugin(c.Params.PluginId) if err != nil { c.Err = err return diff --git a/api4/plugin_test.go b/api4/plugin_test.go index 9751aed198..e28ee48d19 100644 --- a/api4/plugin_test.go +++ b/api4/plugin_test.go @@ -94,7 +94,7 @@ func TestPlugin(t *testing.T) { assert.Equal(t, "testplugin", manifest.Id) }) - th.App.RemovePlugin(manifest.Id) + th.App.Channels().RemovePlugin(manifest.Id) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.PluginSettings.Enable = false }) diff --git a/app/app.go b/app/app.go index ca943e5b49..ca7dd7cc02 100644 --- a/app/app.go +++ b/app/app.go @@ -81,6 +81,9 @@ func (s *Server) getFirstServerRunTimestamp() (int64, *model.AppError) { return value, nil } +func (a *App) Channels() *Channels { + return a.ch +} func (a *App) Srv() *Server { return a.ch.srv } diff --git a/app/app_iface.go b/app/app_iface.go index d9733e9ef4..3579e96b82 100644 --- a/app/app_iface.go +++ b/app/app_iface.go @@ -212,13 +212,8 @@ type AppIface interface { HubRegister(webConn *WebConn) // HubUnregister unregisters a connection from a hub. HubUnregister(webConn *WebConn) - // InstallMarketplacePlugin installs a plugin listed in the marketplace server. It will get the plugin bundle - // from the prepackaged folder, if available, or remotely if EnableRemoteMarketplace is true. - InstallMarketplacePlugin(request *model.InstallMarketplacePluginRequest) (*model.Manifest, *model.AppError) // InstallPlugin unpacks and installs a plugin but does not enable or activate it. InstallPlugin(pluginFile io.ReadSeeker, replace bool) (*model.Manifest, *model.AppError) - // InstallPluginWithSignature verifies and installs plugin. - InstallPluginWithSignature(pluginFile, signature io.ReadSeeker) (*model.Manifest, *model.AppError) // LimitedClientConfigWithComputed gets the configuration in a format suitable for sending to the client. LimitedClientConfigWithComputed() map[string]string // LogAuditRec logs an audit record using default LvlAuditCLI. @@ -414,6 +409,7 @@ type AppIface interface { BulkImportWithPath(c *request.Context, jsonlReader io.Reader, attachmentsReader *zip.Reader, dryRun bool, workers int, importPath string) (*model.AppError, int) CancelJob(jobId string) *model.AppError ChannelMembersToRemove(teamID *string) ([]*model.ChannelMember, *model.AppError) + Channels() *Channels CheckAndSendUserLimitWarningEmails(c *request.Context) *model.AppError CheckCanInviteToSharedChannel(channelId string) error CheckCloudAccountAtLimit() (bool, *model.AppError) @@ -809,7 +805,6 @@ type AppIface interface { ImageProxyRemover() (f func(string) string) ImportPermissions(jsonl io.Reader) error InitPlugins(c *request.Context, pluginDir, webappPluginDir string) - InstallPluginFromData(data model.PluginEventData) InvalidateAllEmailInvites() *model.AppError InvalidateCacheForUser(userID string) InviteGuestsToChannels(teamID string, guestsInvite *model.GuestsInvite, senderId string) *model.AppError @@ -899,8 +894,6 @@ type AppIface interface { RemoveFile(path string) *model.AppError RemoveLdapPrivateCertificate() *model.AppError RemoveLdapPublicCertificate() *model.AppError - RemovePlugin(id string) *model.AppError - RemovePluginFromData(data model.PluginEventData) RemoveRecentCustomStatus(userID string, status *model.CustomStatus) *model.AppError RemoveSamlIdpCertificate() *model.AppError RemoveSamlPrivateCertificate() *model.AppError @@ -990,7 +983,6 @@ type AppIface interface { SetPluginKey(pluginID string, key string, value []byte) *model.AppError SetPluginKeyWithExpiry(pluginID string, key string, value []byte, expireInSeconds int64) *model.AppError SetPluginKeyWithOptions(pluginID string, key string, value []byte, options model.PluginKVSetOptions) (bool, *model.AppError) - SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) SetProfileImage(userID string, imageData *multipart.FileHeader) *model.AppError SetProfileImageFromFile(userID string, file io.Reader) *model.AppError SetProfileImageFromMultiPartFile(userID string, file multipart.File) *model.AppError @@ -1014,7 +1006,6 @@ type AppIface interface { SwitchEmailToOAuth(w http.ResponseWriter, r *http.Request, email, password, code, service string) (string, *model.AppError) SwitchLdapToEmail(ldapPassword, code, email, newPassword string) (string, *model.AppError) SwitchOAuthToEmail(email, password, requesterId string) (string, *model.AppError) - SyncPluginsActiveState() TeamMembersToRemove(teamID *string) ([]*model.TeamMember, *model.AppError) TelemetryId() string TestElasticsearch(cfg *model.Config) *model.AppError @@ -1028,7 +1019,6 @@ type AppIface interface { TotalWebsocketConnections() int TriggerWebhook(c *request.Context, payload *model.OutgoingWebhookPayload, hook *model.OutgoingWebhook, post *model.Post, channel *model.Channel) UnregisterPluginCommand(pluginID, teamID, trigger string) - UnregisterPluginCommands(pluginID string) UpdateActive(c *request.Context, user *model.User, active bool) (*model.User, *model.AppError) UpdateChannelLastViewedAt(channelIDs []string, userID string) *model.AppError UpdateChannelMemberNotifyProps(data map[string]string, channelID string, userID string) (*model.ChannelMember, *model.AppError) diff --git a/app/channels.go b/app/channels.go index 82c17e91d5..d15d4860b0 100644 --- a/app/channels.go +++ b/app/channels.go @@ -18,9 +18,11 @@ import ( type Channels struct { srv *Server + pluginCommandsLock sync.RWMutex + pluginCommands []*PluginCommand + pluginsLock sync.RWMutex pluginsEnvironment *plugin.Environment pluginConfigListenerID string - pluginsLock sync.RWMutex imageProxy *imageproxy.ImageProxy @@ -44,10 +46,17 @@ func init() { } func NewChannels(s *Server) (*Channels, error) { - return &Channels{ + ch := &Channels{ srv: s, imageProxy: imageproxy.MakeImageProxy(s, s.httpService, s.Log), - }, nil + } + // Setup routes. + pluginsRoute := ch.srv.Router.PathPrefix("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter() + pluginsRoute.HandleFunc("", ch.ServePluginRequest) + pluginsRoute.HandleFunc("/public/{public_file:.*}", ch.ServePluginPublicRequest) + pluginsRoute.HandleFunc("/{anything:.*}", ch.ServePluginRequest) + + return ch, nil } func (ch *Channels) Start() error { diff --git a/app/integration_action.go b/app/integration_action.go index a09713c609..7a9e4644bb 100644 --- a/app/integration_action.go +++ b/app/integration_action.go @@ -423,7 +423,7 @@ func (a *App) doPluginRequest(c *request.Context, method, rawURL string, values params["plugin_id"] = pluginID r = mux.SetURLVars(r, params) - a.ch.srv.ServePluginRequest(w, r) + a.ch.ServePluginRequest(w, r) resp := &http.Response{ StatusCode: w.status, diff --git a/app/notification_push_test.go b/app/notification_push_test.go index 6a8538b6f8..297bc12304 100644 --- a/app/notification_push_test.go +++ b/app/notification_push_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -1381,6 +1382,7 @@ func TestPushNotificationRace(t *testing.T) { configStore: memoryStore, Store: mockStore, products: make(map[string]Product), + Router: mux.NewRouter(), } ch, err := NewChannels(s) require.NoError(t, err) diff --git a/app/opentracing/opentracing_layer.go b/app/opentracing/opentracing_layer.go index 1f4b8525d5..927b2a3836 100644 --- a/app/opentracing/opentracing_layer.go +++ b/app/opentracing/opentracing_layer.go @@ -1112,6 +1112,23 @@ func (a *OpenTracingAppLayer) ChannelMembersToRemove(teamID *string) ([]*model.C return resultVar0, resultVar1 } +func (a *OpenTracingAppLayer) Channels() *app.Channels { + origCtx := a.ctx + span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.Channels") + + a.ctx = newCtx + a.app.Srv().Store.SetContext(newCtx) + defer func() { + a.app.Srv().Store.SetContext(origCtx) + a.ctx = origCtx + }() + + defer span.Finish() + resultVar0 := a.app.Channels() + + return resultVar0 +} + func (a *OpenTracingAppLayer) CheckAndSendUserLimitWarningEmails(c *request.Context) *model.AppError { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CheckAndSendUserLimitWarningEmails") @@ -10623,28 +10640,6 @@ func (a *OpenTracingAppLayer) InitPlugins(c *request.Context, pluginDir string, a.app.InitPlugins(c, pluginDir, webappPluginDir) } -func (a *OpenTracingAppLayer) InstallMarketplacePlugin(request *model.InstallMarketplacePluginRequest) (*model.Manifest, *model.AppError) { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.InstallMarketplacePlugin") - - a.ctx = newCtx - a.app.Srv().Store.SetContext(newCtx) - defer func() { - a.app.Srv().Store.SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - resultVar0, resultVar1 := a.app.InstallMarketplacePlugin(request) - - if resultVar1 != nil { - span.LogFields(spanlog.Error(resultVar1)) - ext.Error.Set(span, true) - } - - return resultVar0, resultVar1 -} - func (a *OpenTracingAppLayer) InstallPlugin(pluginFile io.ReadSeeker, replace bool) (*model.Manifest, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.InstallPlugin") @@ -10667,43 +10662,6 @@ func (a *OpenTracingAppLayer) InstallPlugin(pluginFile io.ReadSeeker, replace bo return resultVar0, resultVar1 } -func (a *OpenTracingAppLayer) InstallPluginFromData(data model.PluginEventData) { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.InstallPluginFromData") - - a.ctx = newCtx - a.app.Srv().Store.SetContext(newCtx) - defer func() { - a.app.Srv().Store.SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - a.app.InstallPluginFromData(data) -} - -func (a *OpenTracingAppLayer) InstallPluginWithSignature(pluginFile io.ReadSeeker, signature io.ReadSeeker) (*model.Manifest, *model.AppError) { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.InstallPluginWithSignature") - - a.ctx = newCtx - a.app.Srv().Store.SetContext(newCtx) - defer func() { - a.app.Srv().Store.SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - resultVar0, resultVar1 := a.app.InstallPluginWithSignature(pluginFile, signature) - - if resultVar1 != nil { - span.LogFields(spanlog.Error(resultVar1)) - ext.Error.Set(span, true) - } - - return resultVar0, resultVar1 -} - func (a *OpenTracingAppLayer) InvalidateAllEmailInvites() *model.AppError { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.InvalidateAllEmailInvites") @@ -12769,43 +12727,6 @@ func (a *OpenTracingAppLayer) RemoveLdapPublicCertificate() *model.AppError { return resultVar0 } -func (a *OpenTracingAppLayer) RemovePlugin(id string) *model.AppError { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.RemovePlugin") - - a.ctx = newCtx - a.app.Srv().Store.SetContext(newCtx) - defer func() { - a.app.Srv().Store.SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - resultVar0 := a.app.RemovePlugin(id) - - if resultVar0 != nil { - span.LogFields(spanlog.Error(resultVar0)) - ext.Error.Set(span, true) - } - - return resultVar0 -} - -func (a *OpenTracingAppLayer) RemovePluginFromData(data model.PluginEventData) { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.RemovePluginFromData") - - a.ctx = newCtx - a.app.Srv().Store.SetContext(newCtx) - defer func() { - a.app.Srv().Store.SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - a.app.RemovePluginFromData(data) -} - func (a *OpenTracingAppLayer) RemoveRecentCustomStatus(userID string, status *model.CustomStatus) *model.AppError { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.RemoveRecentCustomStatus") @@ -14835,21 +14756,6 @@ func (a *OpenTracingAppLayer) SetPluginKeyWithOptions(pluginID string, key strin return resultVar0, resultVar1 } -func (a *OpenTracingAppLayer) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SetPluginsEnvironment") - - a.ctx = newCtx - a.app.Srv().Store.SetContext(newCtx) - defer func() { - a.app.Srv().Store.SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - a.app.SetPluginsEnvironment(pluginsEnvironment) -} - func (a *OpenTracingAppLayer) SetProfileImage(userID string, imageData *multipart.FileHeader) *model.AppError { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SetProfileImage") @@ -15352,21 +15258,6 @@ func (a *OpenTracingAppLayer) SyncPlugins() *model.AppError { return resultVar0 } -func (a *OpenTracingAppLayer) SyncPluginsActiveState() { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SyncPluginsActiveState") - - a.ctx = newCtx - a.app.Srv().Store.SetContext(newCtx) - defer func() { - a.app.Srv().Store.SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - a.app.SyncPluginsActiveState() -} - func (a *OpenTracingAppLayer) SyncRolesAndMembership(c *request.Context, syncableID string, syncableType model.GroupSyncableType, includeRemovedMembers bool) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SyncRolesAndMembership") @@ -15688,21 +15579,6 @@ func (a *OpenTracingAppLayer) UnregisterPluginCommand(pluginID string, teamID st a.app.UnregisterPluginCommand(pluginID, teamID, trigger) } -func (a *OpenTracingAppLayer) UnregisterPluginCommands(pluginID string) { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UnregisterPluginCommands") - - a.ctx = newCtx - a.app.Srv().Store.SetContext(newCtx) - defer func() { - a.app.Srv().Store.SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - a.app.UnregisterPluginCommands(pluginID) -} - func (a *OpenTracingAppLayer) UpdateActive(c *request.Context, user *model.User, active bool) (*model.User, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateActive") diff --git a/app/plugin.go b/app/plugin.go index 6dcfabb3c0..e772fb80c9 100644 --- a/app/plugin.go +++ b/app/plugin.go @@ -62,15 +62,11 @@ func (a *App) GetPluginsEnvironment() *plugin.Environment { return a.ch.GetPluginsEnvironment() } -func (a *App) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) { - a.ch.pluginsLock.Lock() - defer a.ch.pluginsLock.Unlock() +func (ch *Channels) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) { + ch.pluginsLock.Lock() + defer ch.pluginsLock.Unlock() - a.ch.pluginsEnvironment = pluginsEnvironment -} - -func (a *App) SyncPluginsActiveState() { - a.ch.syncPluginsActiveState() + ch.pluginsEnvironment = pluginsEnvironment } func (ch *Channels) syncPluginsActiveState() { @@ -452,7 +448,7 @@ func (ch *Channels) disablePlugin(id string) *model.AppError { ch.srv.UpdateConfig(func(cfg *model.Config) { cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: false} }) - ch.srv.unregisterPluginCommands(id) + ch.unregisterPluginCommands(id) // This call will implicitly invoke SyncPluginsActiveState which will deactivate disabled plugins. if _, _, err := ch.srv.SaveConfig(ch.srv.Config(), true); err != nil { @@ -994,7 +990,7 @@ func (ch *Channels) installFeatureFlagPlugins() { } } - _, err := ch.installMarketplacePlugin(&model.InstallMarketplacePluginRequest{ + _, err := ch.InstallMarketplacePlugin(&model.InstallMarketplacePluginRequest{ Id: pluginID, Version: version, }) diff --git a/app/plugin_api.go b/app/plugin_api.go index b21ca7d8d2..92403aa8b3 100644 --- a/app/plugin_api.go +++ b/app/plugin_api.go @@ -832,7 +832,7 @@ func (api *PluginAPI) DisablePlugin(id string) *model.AppError { } func (api *PluginAPI) RemovePlugin(id string) *model.AppError { - return api.app.RemovePlugin(id) + return api.app.Channels().RemovePlugin(id) } func (api *PluginAPI) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) { diff --git a/app/plugin_api_test.go b/app/plugin_api_test.go index 3397ed2f67..d70d668d3c 100644 --- a/app/plugin_api_test.go +++ b/app/plugin_api_test.go @@ -119,7 +119,7 @@ func setupMultiPluginAPITest(t *testing.T, pluginCodes []string, pluginManifests }) } - app.SetPluginsEnvironment(env) + app.ch.SetPluginsEnvironment(env) return pluginDir } @@ -854,7 +854,7 @@ func TestPluginAPIGetPlugins(t *testing.T) { require.True(t, activated) pluginManifests = append(pluginManifests, manifest) } - th.App.SetPluginsEnvironment(env) + th.App.ch.SetPluginsEnvironment(env) // Deactivate the last one for testing success := env.Deactivate(pluginIDs[len(pluginIDs)-1]) @@ -928,7 +928,7 @@ func TestInstallPlugin(t *testing.T) { env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, app.Log(), nil) require.NoError(t, err) - app.SetPluginsEnvironment(env) + app.ch.SetPluginsEnvironment(env) backend := filepath.Join(pluginDir, pluginID, "backend.exe") utils.CompileGo(t, pluginCode, backend) @@ -1604,7 +1604,7 @@ func TestAPIMetrics(t *testing.T) { env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, th.App.Log(), metricsMock) require.NoError(t, err) - th.App.SetPluginsEnvironment(env) + th.App.ch.SetPluginsEnvironment(env) pluginID := model.NewId() backend := filepath.Join(pluginDir, pluginID, "backend.exe") diff --git a/app/plugin_commands.go b/app/plugin_commands.go index 47830f9f4d..edc5a2cc2a 100644 --- a/app/plugin_commands.go +++ b/app/plugin_commands.go @@ -54,10 +54,10 @@ func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) err AutocompleteIconData: command.AutocompleteIconData, } - a.Srv().pluginCommandsLock.Lock() - defer a.Srv().pluginCommandsLock.Unlock() + a.ch.pluginCommandsLock.Lock() + defer a.ch.pluginCommandsLock.Unlock() - for _, pc := range a.Srv().pluginCommands { + for _, pc := range a.ch.pluginCommands { if pc.Command.Trigger == command.Trigger && pc.Command.TeamId == command.TeamId { if pc.PluginId == pluginID { pc.Command = command @@ -66,7 +66,7 @@ func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) err } } - a.Srv().pluginCommands = append(a.Srv().pluginCommands, &PluginCommand{ + a.ch.pluginCommands = append(a.ch.pluginCommands, &PluginCommand{ Command: command, PluginId: pluginID, }) @@ -76,41 +76,37 @@ func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) err func (a *App) UnregisterPluginCommand(pluginID, teamID, trigger string) { trigger = strings.ToLower(trigger) - a.Srv().pluginCommandsLock.Lock() - defer a.Srv().pluginCommandsLock.Unlock() + a.ch.pluginCommandsLock.Lock() + defer a.ch.pluginCommandsLock.Unlock() var remaining []*PluginCommand - for _, pc := range a.Srv().pluginCommands { + for _, pc := range a.ch.pluginCommands { if pc.Command.TeamId != teamID || pc.Command.Trigger != trigger { remaining = append(remaining, pc) } } - a.Srv().pluginCommands = remaining + a.ch.pluginCommands = remaining } -func (a *App) UnregisterPluginCommands(pluginID string) { - a.Srv().unregisterPluginCommands(pluginID) -} - -func (s *Server) unregisterPluginCommands(pluginID string) { - s.pluginCommandsLock.Lock() - defer s.pluginCommandsLock.Unlock() +func (ch *Channels) unregisterPluginCommands(pluginID string) { + ch.pluginCommandsLock.Lock() + defer ch.pluginCommandsLock.Unlock() var remaining []*PluginCommand - for _, pc := range s.pluginCommands { + for _, pc := range ch.pluginCommands { if pc.PluginId != pluginID { remaining = append(remaining, pc) } } - s.pluginCommands = remaining + ch.pluginCommands = remaining } func (a *App) PluginCommandsForTeam(teamID string) []*model.Command { - a.Srv().pluginCommandsLock.RLock() - defer a.Srv().pluginCommandsLock.RUnlock() + a.ch.pluginCommandsLock.RLock() + defer a.ch.pluginCommandsLock.RUnlock() var commands []*model.Command - for _, pc := range a.Srv().pluginCommands { + for _, pc := range a.ch.pluginCommands { if pc.Command.TeamId == "" || pc.Command.TeamId == teamID { commands = append(commands, pc.Command) } @@ -126,14 +122,14 @@ func (a *App) tryExecutePluginCommand(c *request.Context, args *model.CommandArg trigger = strings.ToLower(trigger) var matched *PluginCommand - a.Srv().pluginCommandsLock.RLock() - for _, pc := range a.Srv().pluginCommands { + a.ch.pluginCommandsLock.RLock() + for _, pc := range a.ch.pluginCommands { if (pc.Command.TeamId == "" || pc.Command.TeamId == args.TeamId) && pc.Command.Trigger == trigger { matched = pc break } } - a.Srv().pluginCommandsLock.RUnlock() + a.ch.pluginCommandsLock.RUnlock() if matched == nil { return nil, nil, nil } diff --git a/app/plugin_commands_test.go b/app/plugin_commands_test.go index f53b3c8e44..f611f85364 100644 --- a/app/plugin_commands_test.go +++ b/app/plugin_commands_test.go @@ -106,7 +106,7 @@ func TestPluginCommand(t *testing.T) { require.NotEqual(t, "plugin", commands.Trigger) } - th.App.RemovePlugin(pluginIDs[0]) + th.App.ch.RemovePlugin(pluginIDs[0]) }) t.Run("re-entrant command registration on config change", func(t *testing.T) { @@ -207,7 +207,7 @@ func TestPluginCommand(t *testing.T) { killed = true } - th.App.RemovePlugin(pluginIDs[0]) + th.App.ch.RemovePlugin(pluginIDs[0]) require.False(t, killed, "execute command appears to have deadlocked") }) @@ -285,7 +285,7 @@ func TestPluginCommand(t *testing.T) { require.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType) require.Equal(t, "text", resp.Text) - th.App.RemovePlugin(pluginIDs[0]) + th.App.ch.RemovePlugin(pluginIDs[0]) }) t.Run("plugin has crashed before execution of command", func(t *testing.T) { tearDown, pluginIDs, activationErrors := SetAppEnvironmentWithPlugins(t, []string{` @@ -329,7 +329,7 @@ func TestPluginCommand(t *testing.T) { require.Nil(t, resp) require.NotNil(t, err) require.Equal(t, err.Id, "model.plugin_command_error.error.app_error") - th.App.RemovePlugin(pluginIDs[0]) + th.App.ch.RemovePlugin(pluginIDs[0]) }) t.Run("plugin has crashed due to the execution of the command", func(t *testing.T) { @@ -374,7 +374,7 @@ func TestPluginCommand(t *testing.T) { require.Nil(t, resp) require.NotNil(t, err) require.Equal(t, err.Id, "model.plugin_command_crash.error.app_error") - th.App.RemovePlugin(pluginIDs[0]) + th.App.ch.RemovePlugin(pluginIDs[0]) }) } diff --git a/app/plugin_hooks_test.go b/app/plugin_hooks_test.go index e8d9a1397c..581d583aa8 100644 --- a/app/plugin_hooks_test.go +++ b/app/plugin_hooks_test.go @@ -37,7 +37,7 @@ func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, a env, err := plugin.NewEnvironment(apiFunc, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, app.Log(), nil) require.NoError(t, err) - app.SetPluginsEnvironment(env) + app.ch.SetPluginsEnvironment(env) pluginIDs := []string{} activationErrors := []error{} for _, code := range pluginCode { @@ -1048,7 +1048,7 @@ func TestHookMetrics(t *testing.T) { env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, th.App.Log(), metricsMock) require.NoError(t, err) - th.App.SetPluginsEnvironment(env) + th.App.ch.SetPluginsEnvironment(env) pluginID := model.NewId() backend := filepath.Join(pluginDir, pluginID, "backend.exe") diff --git a/app/plugin_install.go b/app/plugin_install.go index 185a6fcedd..179f52ff4d 100644 --- a/app/plugin_install.go +++ b/app/plugin_install.go @@ -61,10 +61,6 @@ const managedPluginFileName = ".filestore" // fileStorePluginFolder is the folder name in the file store of the plugin bundles installed. const fileStorePluginFolder = "plugins" -func (a *App) InstallPluginFromData(data model.PluginEventData) { - a.ch.installPluginFromData(data) -} - func (ch *Channels) installPluginFromData(data model.PluginEventData) { mlog.Debug("Installing plugin as per cluster message", mlog.String("plugin_id", data.Id)) @@ -111,10 +107,6 @@ func (ch *Channels) installPluginFromData(data model.PluginEventData) { } } -func (a *App) RemovePluginFromData(data model.PluginEventData) { - a.ch.removePluginFromData(data) -} - func (ch *Channels) removePluginFromData(data model.PluginEventData) { mlog.Debug("Removing plugin as per cluster message", mlog.String("plugin_id", data.Id)) @@ -128,10 +120,6 @@ func (ch *Channels) removePluginFromData(data model.PluginEventData) { } // InstallPluginWithSignature verifies and installs plugin. -func (a *App) InstallPluginWithSignature(pluginFile, signature io.ReadSeeker) (*model.Manifest, *model.AppError) { - return a.ch.installPluginWithSignature(pluginFile, signature) -} - func (ch *Channels) installPluginWithSignature(pluginFile, signature io.ReadSeeker) (*model.Manifest, *model.AppError) { return ch.installPlugin(pluginFile, signature, installPluginLocallyAlways) } @@ -189,11 +177,7 @@ func (ch *Channels) installPlugin(pluginFile, signature io.ReadSeeker, installat // InstallMarketplacePlugin installs a plugin listed in the marketplace server. It will get the plugin bundle // from the prepackaged folder, if available, or remotely if EnableRemoteMarketplace is true. -func (a *App) InstallMarketplacePlugin(request *model.InstallMarketplacePluginRequest) (*model.Manifest, *model.AppError) { - return a.ch.installMarketplacePlugin(request) -} - -func (ch *Channels) installMarketplacePlugin(request *model.InstallMarketplacePluginRequest) (*model.Manifest, *model.AppError) { +func (ch *Channels) InstallMarketplacePlugin(request *model.InstallMarketplacePluginRequest) (*model.Manifest, *model.AppError) { var pluginFile, signatureFile io.ReadSeeker prepackagedPlugin, appErr := ch.getPrepackagedPlugin(request.Id, request.Version) @@ -257,10 +241,6 @@ const ( installPluginLocallyAlways ) -func (a *App) installPluginLocally(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) { - return a.ch.installPluginLocally(pluginFile, signature, installationStrategy) -} - func (ch *Channels) installPluginLocally(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) { pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { @@ -412,11 +392,7 @@ func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginD return manifest, nil } -func (a *App) RemovePlugin(id string) *model.AppError { - return a.ch.removePlugin(id) -} - -func (ch *Channels) removePlugin(id string) *model.AppError { +func (ch *Channels) RemovePlugin(id string) *model.AppError { // Disable plugin before removal to make sure this // plugin remains disabled on re-install. if err := ch.disablePlugin(id); err != nil { @@ -457,10 +433,6 @@ func (ch *Channels) removePlugin(id string) *model.AppError { return nil } -func (a *App) removePluginLocally(id string) *model.AppError { - return a.ch.removePluginLocally(id) -} - func (ch *Channels) removePluginLocally(id string) *model.AppError { pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { @@ -488,7 +460,7 @@ func (ch *Channels) removePluginLocally(id string) *model.AppError { pluginsEnvironment.Deactivate(id) pluginsEnvironment.RemovePlugin(id) - ch.srv.unregisterPluginCommands(id) + ch.unregisterPluginCommands(id) if err := os.RemoveAll(pluginPath); err != nil { return model.NewAppError("removePlugin", "app.plugin.remove.app_error", nil, err.Error(), http.StatusInternalServerError) diff --git a/app/plugin_install_test.go b/app/plugin_install_test.go index 69abe2ae66..a3eb6cfb2d 100644 --- a/app/plugin_install_test.go +++ b/app/plugin_install_test.go @@ -73,7 +73,7 @@ func TestInstallPluginLocally(t *testing.T) { th := Setup(t) defer th.TearDown() - actualManifest, appErr := th.App.installPluginLocally(&nilReadSeeker{}, nil, installPluginLocallyOnlyIfNew) + actualManifest, appErr := th.App.ch.installPluginLocally(&nilReadSeeker{}, nil, installPluginLocallyOnlyIfNew) require.NotNil(t, appErr) assert.Equal(t, "app.plugin.extract.app_error", appErr.Id, appErr.Error()) require.Nil(t, actualManifest) @@ -87,7 +87,7 @@ func TestInstallPluginLocally(t *testing.T) { {"test", "test file"}, }) - actualManifest, appErr := th.App.installPluginLocally(reader, nil, installPluginLocallyOnlyIfNew) + actualManifest, appErr := th.App.ch.installPluginLocally(reader, nil, installPluginLocallyOnlyIfNew) require.NotNil(t, appErr) assert.Equal(t, "app.plugin.manifest.app_error", appErr.Id, appErr.Error()) require.Nil(t, actualManifest) @@ -106,7 +106,7 @@ func TestInstallPluginLocally(t *testing.T) { {"plugin.json", string(manifestJSON)}, }) - actualManifest, appError := th.App.installPluginLocally(reader, nil, installationStrategy) + actualManifest, appError := th.App.ch.installPluginLocally(reader, nil, installationStrategy) if actualManifest != nil { require.Equal(t, manifest, actualManifest) } @@ -134,7 +134,7 @@ func TestInstallPluginLocally(t *testing.T) { require.NoError(t, err) for _, bundleInfo := range bundleInfos { - err := th.App.removePluginLocally(bundleInfo.Manifest.Id) + err := th.App.ch.removePluginLocally(bundleInfo.Manifest.Id) require.Nilf(t, err, "failed to remove existing plugin %s", bundleInfo.Manifest.Id) } } diff --git a/app/plugin_requests.go b/app/plugin_requests.go index c9d48a6307..fcb6ae9950 100644 --- a/app/plugin_requests.go +++ b/app/plugin_requests.go @@ -20,11 +20,11 @@ import ( "github.com/mattermost/mattermost-server/v6/utils" ) -func (s *Server) ServePluginRequest(w http.ResponseWriter, r *http.Request) { - pluginsEnvironment := s.Channels().GetPluginsEnvironment() +func (ch *Channels) ServePluginRequest(w http.ResponseWriter, r *http.Request) { + pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { err := model.NewAppError("ServePluginRequest", "app.plugin.disabled.app_error", nil, "Enable plugins to serve plugin requests", http.StatusNotImplemented) - s.Log.Error(err.Error()) + mlog.Error(err.Error()) w.WriteHeader(err.StatusCode) w.Header().Set("Content-Type", "application/json") w.Write([]byte(err.ToJSON())) @@ -34,7 +34,7 @@ func (s *Server) ServePluginRequest(w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) hooks, err := pluginsEnvironment.HooksForPlugin(params["plugin_id"]) if err != nil { - s.Log.Error("Access to route for non-existent plugin", + mlog.Error("Access to route for non-existent plugin", mlog.String("missing_plugin_id", params["plugin_id"]), mlog.String("url", r.URL.String()), mlog.Err(err)) @@ -42,7 +42,7 @@ func (s *Server) ServePluginRequest(w http.ResponseWriter, r *http.Request) { return } - s.servePluginRequest(w, r, hooks.ServeHTTP) + ch.servePluginRequest(w, r, hooks.ServeHTTP) } func (a *App) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string) { @@ -80,7 +80,7 @@ func (a *App) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, so // ServePluginPublicRequest serves public plugin files // at the URL http(s)://$SITE_URL/plugins/$PLUGIN_ID/public/{anything} -func (s *Server) ServePluginPublicRequest(w http.ResponseWriter, r *http.Request) { +func (ch *Channels) ServePluginPublicRequest(w http.ResponseWriter, r *http.Request) { if strings.HasSuffix(r.URL.Path, "/") { http.NotFound(w, r) return @@ -90,7 +90,7 @@ func (s *Server) ServePluginPublicRequest(w http.ResponseWriter, r *http.Request vars := mux.Vars(r) pluginID := vars["plugin_id"] - pluginsEnv := s.Channels().GetPluginsEnvironment() + pluginsEnv := ch.GetPluginsEnvironment() // Check if someone has nullified the pluginsEnv in the meantime if pluginsEnv == nil { @@ -114,11 +114,11 @@ func (s *Server) ServePluginPublicRequest(w http.ResponseWriter, r *http.Request http.ServeFile(w, r, publicFile) } -func (s *Server) servePluginRequest(w http.ResponseWriter, r *http.Request, handler func(*plugin.Context, http.ResponseWriter, *http.Request)) { +func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, handler func(*plugin.Context, http.ResponseWriter, *http.Request)) { token := "" context := &plugin.Context{ RequestId: model.NewId(), - IPAddress: utils.GetIPAddress(r, s.Config().ServiceSettings.TrustedProxyIPHeader), + IPAddress: utils.GetIPAddress(r, ch.srv.Config().ServiceSettings.TrustedProxyIPHeader), AcceptLanguage: r.Header.Get("Accept-Language"), UserAgent: r.UserAgent(), } @@ -141,8 +141,8 @@ func (s *Server) servePluginRequest(w http.ResponseWriter, r *http.Request, hand r.Header.Del("Mattermost-User-Id") if token != "" { - session, err := New(ServerConnector(s.Channels())).GetSession(token) - defer s.userService.ReturnSessionToPool(session) + session, err := New(ServerConnector(ch)).GetSession(token) + defer ch.srv.userService.ReturnSessionToPool(session) csrfCheckPassed := false @@ -183,10 +183,10 @@ func (s *Server) servePluginRequest(w http.ResponseWriter, r *http.Request, hand mlog.String("user_id", userID), } - if *s.Config().ServiceSettings.ExperimentalStrictCSRFEnforcement { - s.Log.Warn(csrfErrorMessage, fields...) + if *ch.srv.Config().ServiceSettings.ExperimentalStrictCSRFEnforcement { + mlog.Warn(csrfErrorMessage, fields...) } else { - s.Log.Debug(csrfErrorMessage, fields...) + mlog.Debug(csrfErrorMessage, fields...) csrfCheckPassed = true } } @@ -212,7 +212,7 @@ func (s *Server) servePluginRequest(w http.ResponseWriter, r *http.Request, hand params := mux.Vars(r) - subpath, _ := utils.GetSubpathFromConfig(s.Config()) + subpath, _ := utils.GetSubpathFromConfig(ch.srv.Config()) newQuery := r.URL.Query() newQuery.Del("access_token") diff --git a/app/plugin_requests_test.go b/app/plugin_requests_test.go index 20a6fc5af1..c41c70be6d 100644 --- a/app/plugin_requests_test.go +++ b/app/plugin_requests_test.go @@ -24,7 +24,7 @@ func TestServePluginPublicRequest(t *testing.T) { require.NoError(t, err) rr := httptest.NewRecorder() - handler := http.HandlerFunc(th.App.Srv().ServePluginPublicRequest) + handler := http.HandlerFunc(th.App.ch.ServePluginPublicRequest) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusNotFound, rr.Code) diff --git a/app/plugin_test.go b/app/plugin_test.go index 946ab93cd2..8c395a0b93 100644 --- a/app/plugin_test.go +++ b/app/plugin_test.go @@ -342,7 +342,7 @@ func TestServePluginRequest(t *testing.T) { w := httptest.NewRecorder() r := httptest.NewRequest("GET", "/plugins/foo/bar", nil) - th.App.ch.srv.ServePluginRequest(w, r) + th.App.ch.ServePluginRequest(w, r) assert.Equal(t, http.StatusNotImplemented, w.Result().StatusCode) } @@ -386,7 +386,7 @@ func TestPrivateServePluginRequest(t *testing.T) { request = mux.SetURLVars(request, map[string]string{"plugin_id": "id"}) - th.App.ch.srv.servePluginRequest(recorder, request, handler) + th.App.ch.servePluginRequest(recorder, request, handler) }) } @@ -409,7 +409,7 @@ func TestHandlePluginRequest(t *testing.T) { var assertions func(*http.Request) router := mux.NewRouter() router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}/{anything:.*}", func(_ http.ResponseWriter, r *http.Request) { - th.App.ch.srv.servePluginRequest(nil, r, func(_ *plugin.Context, _ http.ResponseWriter, r *http.Request) { + th.App.ch.servePluginRequest(nil, r, func(_ *plugin.Context, _ http.ResponseWriter, r *http.Request) { assertions(r) }) }) @@ -621,7 +621,7 @@ func TestPluginSync(t *testing.T) { appErr = th.App.DeletePublicKey("pub_key") checkNoError(t, appErr) - appErr = th.App.RemovePlugin("testplugin") + appErr = th.App.ch.RemovePlugin("testplugin") checkNoError(t, appErr) }) }) @@ -758,7 +758,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) { require.NoError(t, err) require.NotNil(t, pluginBytes) - manifest, appErr := th.App.installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways) + manifest, appErr := th.App.ch.installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways) require.Nil(t, appErr) require.Equal(t, "testplugin", manifest.Id) @@ -785,7 +785,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) { require.Len(t, pluginStatus, 1) require.Equal(t, pluginStatus[0].PluginId, "testplugin") - appErr = th.App.RemovePlugin("testplugin") + appErr = th.App.ch.RemovePlugin("testplugin") checkNoError(t, appErr) pluginStatus, err = env.Statuses() @@ -866,7 +866,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) { require.NoError(t, err) require.NotNil(t, pluginBytes) - manifest, appErr := th.App.installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways) + manifest, appErr := th.App.ch.installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways) require.Nil(t, appErr) require.Equal(t, "testplugin", manifest.Id) @@ -896,7 +896,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) { require.Len(t, pluginStatus, 1) require.Equal(t, pluginStatus[0].PluginId, "testplugin") - appErr = th.App.RemovePlugin("testplugin") + appErr = th.App.ch.RemovePlugin("testplugin") checkNoError(t, appErr) pluginStatus, err = env.Statuses() diff --git a/app/server.go b/app/server.go index 422b2b1dd3..f2d47c4abd 100644 --- a/app/server.go +++ b/app/server.go @@ -142,9 +142,6 @@ type Server struct { configStore *config.Store postActionCookieSecret []byte - pluginCommands []*PluginCommand - pluginCommandsLock sync.RWMutex - telemetryService *telemetry.TelemetryService userService *users.UserService teamService *teams.TeamService @@ -213,6 +210,7 @@ func NewServer(options ...Option) (*Server, error) { licenseListeners: map[string]func(*model.License, *model.License){}, hashSeed: maphash.MakeSeed(), uploadLockMap: map[string]bool{}, + timezones: timezones.New(), products: make(map[string]Product), } @@ -245,6 +243,12 @@ func NewServer(options ...Option) (*Server, error) { mlog.Error("Could not initiate logging", mlog.Err(err)) } + subpath, err := utils.GetSubpathFromConfig(s.Config()) + if err != nil { + return nil, errors.Wrap(err, "failed to parse SiteURL subpath") + } + s.Router = s.RootRouter.PathPrefix(subpath).Subrouter() + // This is called after initLogging() to avoid a race condition. mlog.Info("Server is initializing...", mlog.String("go_version", runtime.Version())) @@ -253,9 +257,9 @@ func NewServer(options ...Option) (*Server, error) { // Step 3: Initialize products. // Depends on s.httpService. for name, initializer := range products { - prod, err := initializer(s) - if err != nil { - return nil, errors.Wrapf(err, "error initializing product: %s", name) + prod, err2 := initializer(s) + if err2 != nil { + return nil, errors.Wrapf(err2, "error initializing product: %s", name) } s.products[name] = prod @@ -280,8 +284,8 @@ func NewServer(options ...Option) (*Server, error) { // At the moment we only have this implementation // in the future the cache provider will be built based on the loaded config s.CacheProvider = cache.NewProvider() - if err := s.CacheProvider.Connect(); err != nil { - return nil, errors.Wrapf(err, "Unable to connect to cache provider") + if err2 := s.CacheProvider.Connect(); err2 != nil { + return nil, errors.Wrapf(err2, "Unable to connect to cache provider") } // It is important to initialize the hub only after the global logger is set @@ -326,7 +330,6 @@ func NewServer(options ...Option) (*Server, error) { } } - var err error s.Store, err = s.newStore() if err != nil { return nil, errors.Wrap(err, "cannot create store") @@ -532,17 +535,6 @@ func NewServer(options ...Option) (*Server, error) { return nil, errors.Wrapf(err, "unable to ensure first run timestamp") } - subpath, err := utils.GetSubpathFromConfig(s.Config()) - if err != nil { - return nil, errors.Wrap(err, "failed to parse SiteURL subpath") - } - s.Router = s.RootRouter.PathPrefix(subpath).Subrouter() - - pluginsRoute := s.Router.PathPrefix("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter() - pluginsRoute.HandleFunc("", s.ServePluginRequest) - pluginsRoute.HandleFunc("/public/{public_file:.*}", s.ServePluginPublicRequest) - pluginsRoute.HandleFunc("/{anything:.*}", s.ServePluginRequest) - // If configured with a subpath, redirect 404s at the root back into the subpath. if subpath != "/" { s.RootRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -580,7 +572,6 @@ func NewServer(options ...Option) (*Server, error) { } } - s.timezones = timezones.New() // Start email batching because it's not like the other jobs s.AddConfigListener(func(_, _ *model.Config) { s.EmailService.InitEmailBatching() diff --git a/web/web_test.go b/web/web_test.go index c2d85183ca..0e06a33cf0 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -327,7 +327,7 @@ func TestPublicFilesRequest(t *testing.T) { require.NotNil(t, manifest) require.True(t, activated) - th.App.SetPluginsEnvironment(env) + th.App.Channels().SetPluginsEnvironment(env) req, _ := http.NewRequest("GET", "/plugins/com.mattermost.sample/public/hello.html", nil) res := httptest.NewRecorder()