diff --git a/api4/post.go b/api4/post.go index b5c33488fa..eb2dc1a26b 100644 --- a/api4/post.go +++ b/api4/post.go @@ -898,7 +898,13 @@ func getFileInfosForPost(c *Context, w http.ResponseWriter, r *http.Request) { return } - infos, err := c.App.GetFileInfosForPostWithMigration(c.Params.PostId) + includeDeleted, _ := strconv.ParseBool(r.URL.Query().Get("include_deleted")) + if includeDeleted && !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) { + c.SetPermissionError(model.PermissionManageSystem) + return + } + + infos, err := c.App.GetFileInfosForPostWithMigration(c.Params.PostId, includeDeleted) if err != nil { c.Err = err return diff --git a/api4/post_test.go b/api4/post_test.go index 5db9de4b24..22006fb812 100644 --- a/api4/post_test.go +++ b/api4/post_test.go @@ -2623,6 +2623,39 @@ func TestGetFileInfosForPost(t *testing.T) { require.Error(t, err) CheckForbiddenStatus(t, resp) + // Delete post + th.SystemAdminClient.DeletePost(post.Id) + + // Normal client should get 404 when trying to access deleted post normally + _, resp, err = client.GetFileInfosForPost(post.Id, "") + require.Error(t, err) + CheckNotFoundStatus(t, resp) + + // Normal client should get unauthorized when trying to access deleted post + _, resp, err = client.GetFileInfosForPostIncludeDeleted(post.Id, "") + require.Error(t, err) + CheckForbiddenStatus(t, resp) + + // System client should get 404 when trying to access deleted post normally + _, resp, err = th.SystemAdminClient.GetFileInfosForPost(post.Id, "") + require.Error(t, err) + CheckNotFoundStatus(t, resp) + + // System client should be able to access deleted post with include_deleted param + infos, _, err = th.SystemAdminClient.GetFileInfosForPostIncludeDeleted(post.Id, "") + require.NoError(t, err) + + require.Len(t, infos, 3, "missing file infos") + + found = false + for _, info := range infos { + if info.Id == fileIds[0] { + found = true + } + } + + require.True(t, found, "missing file info") + client.Logout() _, resp, err = client.GetFileInfosForPost(model.NewId(), "") require.Error(t, err) diff --git a/app/app_iface.go b/app/app_iface.go index e1e0aeb057..2ac622e3c0 100644 --- a/app/app_iface.go +++ b/app/app_iface.go @@ -630,8 +630,8 @@ type AppIface interface { GetFile(fileID string) ([]byte, *model.AppError) GetFileInfo(fileID string) (*model.FileInfo, *model.AppError) GetFileInfos(page, perPage int, opt *model.GetFileInfosOptions) ([]*model.FileInfo, *model.AppError) - GetFileInfosForPost(postID string, fromMaster bool) ([]*model.FileInfo, *model.AppError) - GetFileInfosForPostWithMigration(postID string) ([]*model.FileInfo, *model.AppError) + GetFileInfosForPost(postID string, fromMaster bool, includeDeleted bool) ([]*model.FileInfo, *model.AppError) + GetFileInfosForPostWithMigration(postID string, includeDeleted bool) ([]*model.FileInfo, *model.AppError) GetFlaggedPosts(userID string, offset int, limit int) (*model.PostList, *model.AppError) GetFlaggedPostsForChannel(userID, channelID string, offset int, limit int) (*model.PostList, *model.AppError) GetFlaggedPostsForTeam(userID, teamID string, offset int, limit int) (*model.PostList, *model.AppError) diff --git a/app/import_functions.go b/app/import_functions.go index 42ba70e4d9..7ab16c3e50 100644 --- a/app/import_functions.go +++ b/app/import_functions.go @@ -1224,7 +1224,7 @@ func (a *App) importAttachment(c *request.Context, data *AttachmentImportData, p // Go over existing files in the post and see if there already exists a file with the same name, size and hash. If so - skip it if post.Id != "" { - oldFiles, err := a.GetFileInfosForPost(post.Id, true) + oldFiles, err := a.GetFileInfosForPost(post.Id, true, false) if err != nil { return nil, model.NewAppError("BulkImport", "app.import.attachment.file_upload.error", map[string]any{"FilePath": *data.Path}, "", http.StatusBadRequest) } diff --git a/app/opentracing/opentracing_layer.go b/app/opentracing/opentracing_layer.go index d31592efe5..5761473dcf 100644 --- a/app/opentracing/opentracing_layer.go +++ b/app/opentracing/opentracing_layer.go @@ -5970,7 +5970,7 @@ func (a *OpenTracingAppLayer) GetFileInfos(page int, perPage int, opt *model.Get return resultVar0, resultVar1 } -func (a *OpenTracingAppLayer) GetFileInfosForPost(postID string, fromMaster bool) ([]*model.FileInfo, *model.AppError) { +func (a *OpenTracingAppLayer) GetFileInfosForPost(postID string, fromMaster bool, includeDeleted bool) ([]*model.FileInfo, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetFileInfosForPost") @@ -5982,7 +5982,7 @@ func (a *OpenTracingAppLayer) GetFileInfosForPost(postID string, fromMaster bool }() defer span.Finish() - resultVar0, resultVar1 := a.app.GetFileInfosForPost(postID, fromMaster) + resultVar0, resultVar1 := a.app.GetFileInfosForPost(postID, fromMaster, includeDeleted) if resultVar1 != nil { span.LogFields(spanlog.Error(resultVar1)) @@ -5992,7 +5992,7 @@ func (a *OpenTracingAppLayer) GetFileInfosForPost(postID string, fromMaster bool return resultVar0, resultVar1 } -func (a *OpenTracingAppLayer) GetFileInfosForPostWithMigration(postID string) ([]*model.FileInfo, *model.AppError) { +func (a *OpenTracingAppLayer) GetFileInfosForPostWithMigration(postID string, includeDeleted bool) ([]*model.FileInfo, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetFileInfosForPostWithMigration") @@ -6004,7 +6004,7 @@ func (a *OpenTracingAppLayer) GetFileInfosForPostWithMigration(postID string) ([ }() defer span.Finish() - resultVar0, resultVar1 := a.app.GetFileInfosForPostWithMigration(postID) + resultVar0, resultVar1 := a.app.GetFileInfosForPostWithMigration(postID, includeDeleted) if resultVar1 != nil { span.LogFields(spanlog.Error(resultVar1)) diff --git a/app/post.go b/app/post.go index 487537029d..de087c4995 100644 --- a/app/post.go +++ b/app/post.go @@ -1269,6 +1269,8 @@ func (a *App) DeletePost(c request.CTX, postID, deleteByID string) (*model.Post, a.Srv().Go(func() { a.deletePostFiles(post.Id) }) + a.Srv().Store.FileInfo().InvalidateFileInfosForPostCache(postID, true) + a.Srv().Store.FileInfo().InvalidateFileInfosForPostCache(postID, false) } a.Srv().Go(func() { a.deleteFlaggedPosts(post.Id) @@ -1541,16 +1543,16 @@ func (a *App) GetRecentSearchesForUser(userID string) ([]*model.SearchParams, *m return searchParams, nil } -func (a *App) GetFileInfosForPostWithMigration(postID string) ([]*model.FileInfo, *model.AppError) { +func (a *App) GetFileInfosForPostWithMigration(postID string, includeDeleted bool) ([]*model.FileInfo, *model.AppError) { pchan := make(chan store.StoreResult, 1) go func() { - post, err := a.Srv().Store.Post().GetSingle(postID, false) + post, err := a.Srv().Store.Post().GetSingle(postID, includeDeleted) pchan <- store.StoreResult{Data: post, NErr: err} close(pchan) }() - infos, err := a.GetFileInfosForPost(postID, false) + infos, err := a.GetFileInfosForPost(postID, false, includeDeleted) if err != nil { return nil, err } @@ -1580,8 +1582,8 @@ func (a *App) GetFileInfosForPostWithMigration(postID string) ([]*model.FileInfo return infos, nil } -func (a *App) GetFileInfosForPost(postID string, fromMaster bool) ([]*model.FileInfo, *model.AppError) { - fileInfos, err := a.Srv().Store.FileInfo().GetForPost(postID, fromMaster, false, true) +func (a *App) GetFileInfosForPost(postID string, fromMaster bool, includeDeleted bool) ([]*model.FileInfo, *model.AppError) { + fileInfos, err := a.Srv().Store.FileInfo().GetForPost(postID, fromMaster, includeDeleted, true) if err != nil { return nil, model.NewAppError("GetFileInfosForPost", "app.file_info.get_for_post.app_error", nil, err.Error(), http.StatusInternalServerError) } diff --git a/app/post_metadata.go b/app/post_metadata.go index f5ff89cb19..80809c5ee4 100644 --- a/app/post_metadata.go +++ b/app/post_metadata.go @@ -216,7 +216,7 @@ func (a *App) getFileMetadataForPost(post *model.Post, fromMaster bool) ([]*mode return nil, nil } - return a.GetFileInfosForPost(post.Id, fromMaster) + return a.GetFileInfosForPost(post.Id, fromMaster, false) } func (a *App) getEmojisAndReactionsForPost(post *model.Post) ([]*model.Emoji, []*model.Reaction, *model.AppError) { diff --git a/app/post_test.go b/app/post_test.go index bc38d528aa..64c203e8f2 100644 --- a/app/post_test.go +++ b/app/post_test.go @@ -219,7 +219,7 @@ func TestAttachFilesToPost(t *testing.T) { appErr := th.App.attachFilesToPost(post) assert.Nil(t, appErr) - infos, appErr := th.App.GetFileInfosForPost(post.Id, false) + infos, appErr := th.App.GetFileInfosForPost(post.Id, false, false) assert.Nil(t, appErr) assert.Len(t, infos, 2) }) @@ -247,7 +247,7 @@ func TestAttachFilesToPost(t *testing.T) { appErr := th.App.attachFilesToPost(post) assert.Nil(t, appErr) - infos, appErr := th.App.GetFileInfosForPost(post.Id, false) + infos, appErr := th.App.GetFileInfosForPost(post.Id, false, false) assert.Nil(t, appErr) assert.Len(t, infos, 1) assert.Equal(t, info2.Id, infos[0].Id) diff --git a/model/client4.go b/model/client4.go index 76555fd9da..ba374830a1 100644 --- a/model/client4.go +++ b/model/client4.go @@ -4419,6 +4419,24 @@ func (c *Client4) GetFileInfosForPost(postId string, etag string) ([]*FileInfo, return list, BuildResponse(r), nil } +// GetFileInfosForPost gets all the file info objects attached to a post, including deleted +func (c *Client4) GetFileInfosForPostIncludeDeleted(postId string, etag string) ([]*FileInfo, *Response, error) { + r, err := c.DoAPIGet(c.postRoute(postId)+"/files/info"+"?include_deleted="+c.boolString(true), etag) + if err != nil { + return nil, BuildResponse(r), err + } + defer closeBody(r) + + var list []*FileInfo + if r.StatusCode == http.StatusNotModified { + return list, BuildResponse(r), nil + } + if jsonErr := json.NewDecoder(r.Body).Decode(&list); jsonErr != nil { + return nil, nil, NewAppError("GetFileInfosForPostIncludeDeleted", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError) + } + return list, BuildResponse(r), nil +} + // General/System Section // GenerateSupportPacket downloads the generated support packet