diff --git a/server/channels/app/notification_push.go b/server/channels/app/notification_push.go index 9c063e6a49e..ebc6e354f62 100644 --- a/server/channels/app/notification_push.go +++ b/server/channels/app/notification_push.go @@ -71,13 +71,6 @@ func (a *App) sendPushNotificationSync(rctx request.CTX, post *model.Post, user explicitMention bool, channelWideMention bool, replyToThreadType string, ) *model.AppError { cfg := a.Config() - - pushMech := model.AuditMechPushFull - if *cfg.EmailSettings.PushNotificationContents == model.IdLoadedNotification { - pushMech = model.AuditMechPushIDOnly - } - a.AuditRecord(rctx.Context(), user.Id, post.Id, pushMech) - msg, appErr := a.BuildPushNotificationMessage( rctx, *cfg.EmailSettings.PushNotificationContents, @@ -94,6 +87,16 @@ func (a *App) sendPushNotificationSync(rctx request.CTX, post *model.Post, user return appErr } + // Mechanism 6/7: BuildPushNotificationMessage may downgrade ID-loaded to + // generic/full when the license fails (notification_push.go:734-735), so + // derive the audit mechanism from the actual built payload rather than + // the requested config. + pushMech := model.AuditMechPushFull + if msg.IsIdLoaded { + pushMech = model.AuditMechPushIDOnly + } + a.AuditRecord(rctx.Context(), user.Id, post.Id, pushMech) + return a.sendPushNotificationToAllSessions(rctx, msg, user.Id, "") } diff --git a/server/channels/app/post.go b/server/channels/app/post.go index f2722a0b7d9..22d35d692a1 100644 --- a/server/channels/app/post.go +++ b/server/channels/app/post.go @@ -401,10 +401,10 @@ func (a *App) CreatePost(rctx request.CTX, post *model.Post, channel *model.Chan a.Srv().Go(func() { a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool { hooks.MessageHasBeenPosted(pluginContext, pluginPost) - // Mechanism 12: every plugin that registers MessageHasBeenPosted - // receives the post. Attribute to the post author (no per-plugin - // user identity available at this hook). - a.AuditRecord(context.Background(), rpost.UserId, rpost.Id, model.AuditMechPluginHook) + //// Mechanism 12: every plugin that registers MessageHasBeenPosted + //// receives the post. Attribute to the post author (no per-plugin + //// user identity available at this hook). + //a.AuditRecord(context.Background(), rpost.UserId, rpost.Id, model.AuditMechPluginHook) return true }, plugin.MessageHasBeenPostedID) }) diff --git a/server/channels/store/sqlstore/store.go b/server/channels/store/sqlstore/store.go index 434441d1ac3..b5f3e864a3a 100644 --- a/server/channels/store/sqlstore/store.go +++ b/server/channels/store/sqlstore/store.go @@ -400,10 +400,15 @@ func (ss *SqlStore) initConnection() error { } } - // Open the audit-storage pool whenever settings were supplied, regardless - // of Enable. Enable controls runtime writes (via the no-op sub-store), but - // schema provisioning runs unconditionally so the table is always ready. - if ss.asSettings != nil && ss.asSettings.DataSource != nil && *ss.asSettings.DataSource != "" { + // Open the audit-storage pool only when Enable=true. When Enable=false the + // audit DB does not need to be reachable at startup; flipping Enable on + // later requires a server restart so the pool can open and the migration + // can run. + auditStorageEnabled := ss.asSettings != nil && ss.asSettings.Enable != nil && *ss.asSettings.Enable + if auditStorageEnabled { + if ss.asSettings.DataSource == nil || *ss.asSettings.DataSource == "" { + return errors.New("audit-storage is enabled but data source is empty") + } rtHandle, err := sqlUtils.SetupAuditStorageConnection(ss.Logger(), "audit-storage", ss.asSettings, DBPingAttempts) if err != nil { return errors.Wrap(err, "failed to setup audit-storage connection") diff --git a/server/platform/shared/filestore/mocks/blobDownloader.go b/server/platform/shared/filestore/mocks/blobDownloader.go new file mode 100644 index 00000000000..5845c0d91d7 --- /dev/null +++ b/server/platform/shared/filestore/mocks/blobDownloader.go @@ -0,0 +1,60 @@ +// Code generated by mockery v2.53.4. DO NOT EDIT. + +// Regenerate this file using `make filestore-mocks`. + +package mocks + +import ( + context "context" + + blob "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob" + + mock "github.com/stretchr/testify/mock" +) + +// blobDownloader is an autogenerated mock type for the blobDownloader type +type blobDownloader struct { + mock.Mock +} + +// DownloadStream provides a mock function with given fields: ctx, opts +func (_m *blobDownloader) DownloadStream(ctx context.Context, opts *blob.DownloadStreamOptions) (blob.DownloadStreamResponse, error) { + ret := _m.Called(ctx, opts) + + if len(ret) == 0 { + panic("no return value specified for DownloadStream") + } + + var r0 blob.DownloadStreamResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *blob.DownloadStreamOptions) (blob.DownloadStreamResponse, error)); ok { + return rf(ctx, opts) + } + if rf, ok := ret.Get(0).(func(context.Context, *blob.DownloadStreamOptions) blob.DownloadStreamResponse); ok { + r0 = rf(ctx, opts) + } else { + r0 = ret.Get(0).(blob.DownloadStreamResponse) + } + + if rf, ok := ret.Get(1).(func(context.Context, *blob.DownloadStreamOptions) error); ok { + r1 = rf(ctx, opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// newBlobDownloader creates a new instance of blobDownloader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newBlobDownloader(t interface { + mock.TestingT + Cleanup(func()) +}) *blobDownloader { + mock := &blobDownloader{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +}