[MM-42194] Get file information from a deleted post (#20279)
* Introduced inlcude_deleted query parameter to allow admins to retrieve contents of post regardless of deletion status * Introduced new client route and tests for getting file info of deleted posts * Fixed tests due to caching of posts * gofmt * Small formatting updates * Invalidating file infos cache on delete of post if post includes files * Including deleted in migration flow * Moved invalidating of cache Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
bc7f961d75
Коммит
3d7859396d
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
12
app/post.go
12
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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Ссылка в новой задаче
Block a user