Cherry-pick PR for guest-user-file-upload-permission for release-10.11 (#35073)
Automatic Merge
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
0655a63354
Коммит
707f7ba42b
@@ -62,6 +62,13 @@ func createPostChecks(where string, c *Context, post *model.Post) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(post.FileIds) > 0 {
|
||||||
|
if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), post.ChannelId, model.PermissionUploadFile); !ok {
|
||||||
|
c.SetPermissionError(model.PermissionUploadFile)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
postHardenedModeCheckWithContext(where, c, post.GetProps())
|
postHardenedModeCheckWithContext(where, c, post.GetProps())
|
||||||
if c.Err != nil {
|
if c.Err != nil {
|
||||||
return
|
return
|
||||||
@@ -1022,6 +1029,12 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
post.FileIds = originalPost.FileIds
|
post.FileIds = originalPost.FileIds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check upload_file permission only if update is adding NEW files (not just keeping existing ones)
|
||||||
|
checkUploadFilePermissionForNewFiles(c, post.FileIds, originalPost)
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if c.AppContext.Session().UserId != originalPost.UserId {
|
if c.AppContext.Session().UserId != originalPost.UserId {
|
||||||
// We don't need to check the member here, since we already checked it above
|
// We don't need to check the member here, since we already checked it above
|
||||||
if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), originalPost.ChannelId, model.PermissionEditOthersPosts); !ok {
|
if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), originalPost.ChannelId, model.PermissionEditOthersPosts); !ok {
|
||||||
@@ -1090,6 +1103,19 @@ func patchPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
originalPost, err := c.App.GetSinglePost(c.AppContext, c.Params.PostId, false)
|
||||||
|
if err != nil {
|
||||||
|
c.SetPermissionError(model.PermissionEditPost)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if post.FileIds != nil {
|
||||||
|
checkUploadFilePermissionForNewFiles(c, *post.FileIds, originalPost)
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
patchedPost, isMemberForPReviews, err := c.App.PatchPost(c.AppContext, c.Params.PostId, c.App.PostPatchWithProxyRemovedFromImageURLs(&post), nil)
|
patchedPost, isMemberForPReviews, err := c.App.PatchPost(c.AppContext, c.Params.PostId, c.App.PostPatchWithProxyRemovedFromImageURLs(&post), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
|
|||||||
@@ -277,6 +277,46 @@ func TestCreatePost(t *testing.T) {
|
|||||||
assert.Nil(t, rpost)
|
assert.Nil(t, rpost)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("should prevent creating post with files when user lacks upload_file permission in target channel", func(t *testing.T) {
|
||||||
|
fileResp, resp, err := client.UploadFile(context.Background(), []byte("test file data"), th.BasicChannel.Id, "test-file.txt")
|
||||||
|
require.NoError(t, err)
|
||||||
|
CheckCreatedStatus(t, resp)
|
||||||
|
fileId := fileResp.FileInfos[0].Id
|
||||||
|
|
||||||
|
th.RemovePermissionFromRole(model.PermissionUploadFile.Id, model.ChannelUserRoleId)
|
||||||
|
defer func() {
|
||||||
|
th.AddPermissionToRole(model.PermissionUploadFile.Id, model.ChannelUserRoleId)
|
||||||
|
}()
|
||||||
|
|
||||||
|
post := &model.Post{
|
||||||
|
ChannelId: th.BasicChannel.Id,
|
||||||
|
Message: "Test post with file",
|
||||||
|
FileIds: model.StringArray{fileId},
|
||||||
|
}
|
||||||
|
rpost, resp, err := client.CreatePost(context.Background(), post)
|
||||||
|
require.Error(t, err)
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
assert.Nil(t, rpost)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should allow creating post with files when user has upload_file permission", func(t *testing.T) {
|
||||||
|
fileResp, resp, err := client.UploadFile(context.Background(), []byte("test file data"), th.BasicChannel.Id, "test-file.txt")
|
||||||
|
require.NoError(t, err)
|
||||||
|
CheckCreatedStatus(t, resp)
|
||||||
|
fileId := fileResp.FileInfos[0].Id
|
||||||
|
|
||||||
|
post := &model.Post{
|
||||||
|
ChannelId: th.BasicChannel.Id,
|
||||||
|
Message: "Test post with file",
|
||||||
|
FileIds: model.StringArray{fileId},
|
||||||
|
}
|
||||||
|
rpost, resp, err := client.CreatePost(context.Background(), post)
|
||||||
|
require.NoError(t, err)
|
||||||
|
CheckCreatedStatus(t, resp)
|
||||||
|
require.NotNil(t, rpost)
|
||||||
|
assert.Contains(t, rpost.FileIds, fileId)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("CreateAt should match the one provided in the request", func(t *testing.T) {
|
t.Run("CreateAt should match the one provided in the request", func(t *testing.T) {
|
||||||
post := basicPost()
|
post := basicPost()
|
||||||
post.CreateAt = 123
|
post.CreateAt = 123
|
||||||
@@ -1543,6 +1583,62 @@ func TestUpdatePost(t *testing.T) {
|
|||||||
CheckBadRequestStatus(t, resp)
|
CheckBadRequestStatus(t, resp)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("should prevent updating post with files when user lacks upload_file permission in target channel", func(t *testing.T) {
|
||||||
|
postWithoutFiles, _, appErr := th.App.CreatePost(th.Context, &model.Post{
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
ChannelId: channel.Id,
|
||||||
|
Message: "Post without files",
|
||||||
|
}, channel, model.CreatePostFlags{SetOnline: true})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
fileResp, resp, err := client.UploadFile(context.Background(), []byte("test file data"), channel.Id, "test-file.txt")
|
||||||
|
require.NoError(t, err)
|
||||||
|
CheckCreatedStatus(t, resp)
|
||||||
|
fileId := fileResp.FileInfos[0].Id
|
||||||
|
|
||||||
|
th.RemovePermissionFromRole(model.PermissionUploadFile.Id, model.ChannelUserRoleId)
|
||||||
|
defer func() {
|
||||||
|
th.AddPermissionToRole(model.PermissionUploadFile.Id, model.ChannelUserRoleId)
|
||||||
|
}()
|
||||||
|
|
||||||
|
updatePost := &model.Post{
|
||||||
|
Id: postWithoutFiles.Id,
|
||||||
|
ChannelId: channel.Id,
|
||||||
|
Message: "Updated post with file",
|
||||||
|
FileIds: model.StringArray{fileId},
|
||||||
|
}
|
||||||
|
updatedPost, resp, err := client.UpdatePost(context.Background(), postWithoutFiles.Id, updatePost)
|
||||||
|
require.Error(t, err)
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
assert.Nil(t, updatedPost)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should allow updating post with files when user has upload_file permission", func(t *testing.T) {
|
||||||
|
postWithoutFiles, _, appErr := th.App.CreatePost(th.Context, &model.Post{
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
ChannelId: channel.Id,
|
||||||
|
Message: "Post without files",
|
||||||
|
}, channel, model.CreatePostFlags{SetOnline: true})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
fileResp, resp, err := client.UploadFile(context.Background(), []byte("test file data"), channel.Id, "test-file.txt")
|
||||||
|
require.NoError(t, err)
|
||||||
|
CheckCreatedStatus(t, resp)
|
||||||
|
fileId := fileResp.FileInfos[0].Id
|
||||||
|
|
||||||
|
updatePost := &model.Post{
|
||||||
|
Id: postWithoutFiles.Id,
|
||||||
|
ChannelId: channel.Id,
|
||||||
|
Message: "Updated post with file",
|
||||||
|
FileIds: model.StringArray{fileId},
|
||||||
|
}
|
||||||
|
updatedPost, resp, err := client.UpdatePost(context.Background(), postWithoutFiles.Id, updatePost)
|
||||||
|
require.NoError(t, err)
|
||||||
|
CheckOKStatus(t, resp)
|
||||||
|
require.NotNil(t, updatedPost)
|
||||||
|
assert.Contains(t, updatedPost.FileIds, fileId)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("logged out", func(t *testing.T) {
|
t.Run("logged out", func(t *testing.T) {
|
||||||
_, err := client.Logout(context.Background())
|
_, err := client.Logout(context.Background())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -41,3 +41,31 @@ func postPriorityCheckWithContext(where string, c *Context, priority *model.Post
|
|||||||
c.Err = appErr
|
c.Err = appErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkUploadFilePermissionForNewFiles checks upload_file permission only when
|
||||||
|
// adding new files to a post, preventing permission bypass via cross-channel file attachments.
|
||||||
|
func checkUploadFilePermissionForNewFiles(c *Context, newFileIds []string, originalPost *model.Post) {
|
||||||
|
if len(newFileIds) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
originalFileIDsMap := make(map[string]bool, len(originalPost.FileIds))
|
||||||
|
for _, fileID := range originalPost.FileIds {
|
||||||
|
originalFileIDsMap[fileID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNewFiles := false
|
||||||
|
for _, fileID := range newFileIds {
|
||||||
|
if !originalFileIDsMap[fileID] {
|
||||||
|
hasNewFiles = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasNewFiles {
|
||||||
|
if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), originalPost.ChannelId, model.PermissionUploadFile); !ok {
|
||||||
|
c.SetPermissionError(model.PermissionUploadFile)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -74,6 +74,14 @@ func createSchedulePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
defer c.LogAuditRecWithLevel(auditRec, app.LevelContent)
|
defer c.LogAuditRecWithLevel(auditRec, app.LevelContent)
|
||||||
model.AddEventParameterAuditableToAuditRec(auditRec, "scheduledPost", &scheduledPost)
|
model.AddEventParameterAuditableToAuditRec(auditRec, "scheduledPost", &scheduledPost)
|
||||||
|
|
||||||
|
if len(scheduledPost.FileIds) > 0 {
|
||||||
|
hasPermission, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), scheduledPost.ChannelId, model.PermissionUploadFile)
|
||||||
|
if !hasPermission {
|
||||||
|
c.SetPermissionError(model.PermissionUploadFile)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scheduledPostChecks("Api4.createSchedulePost", c, &scheduledPost)
|
scheduledPostChecks("Api4.createSchedulePost", c, &scheduledPost)
|
||||||
if c.Err != nil {
|
if c.Err != nil {
|
||||||
return
|
return
|
||||||
@@ -169,12 +177,38 @@ func updateScheduledPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
defer c.LogAuditRecWithLevel(auditRec, app.LevelContent)
|
defer c.LogAuditRecWithLevel(auditRec, app.LevelContent)
|
||||||
model.AddEventParameterAuditableToAuditRec(auditRec, "scheduledPost", &scheduledPost)
|
model.AddEventParameterAuditableToAuditRec(auditRec, "scheduledPost", &scheduledPost)
|
||||||
|
|
||||||
|
userId := c.AppContext.Session().UserId
|
||||||
|
existingScheduledPost, err := c.App.Srv().Store().ScheduledPost().Get(scheduledPost.Id)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = model.NewAppError("updateScheduledPost", "app.update_scheduled_post.get_scheduled_post.error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if existingScheduledPost == nil {
|
||||||
|
c.Err = model.NewAppError("updateScheduledPost", "app.update_scheduled_post.existing_scheduled_post.not_exist", nil, "", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if existingScheduledPost.UserId != userId {
|
||||||
|
c.Err = model.NewAppError("updateScheduledPost", "app.update_scheduled_post.update_permission.error", nil, "", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(scheduledPost.FileIds) > 0 {
|
||||||
|
originalPost, err := existingScheduledPost.ToPost()
|
||||||
|
if err != nil {
|
||||||
|
c.Err = model.NewAppError("updateScheduledPost", "app.update_scheduled_post.convert_to_post.error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
checkUploadFilePermissionForNewFiles(c, scheduledPost.FileIds, originalPost)
|
||||||
|
if c.Err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scheduledPostChecks("Api4.updateScheduledPost", c, &scheduledPost)
|
scheduledPostChecks("Api4.updateScheduledPost", c, &scheduledPost)
|
||||||
if c.Err != nil {
|
if c.Err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userId := c.AppContext.Session().UserId
|
|
||||||
updatedScheduledPost, appErr := c.App.UpdateScheduledPost(c.AppContext, userId, &scheduledPost, connectionID)
|
updatedScheduledPost, appErr := c.App.UpdateScheduledPost(c.AppContext, userId, &scheduledPost, connectionID)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
c.Err = appErr
|
c.Err = appErr
|
||||||
@@ -209,6 +243,21 @@ func deleteScheduledPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
model.AddEventParameterToAuditRec(auditRec, "scheduledPostId", scheduledPostId)
|
model.AddEventParameterToAuditRec(auditRec, "scheduledPostId", scheduledPostId)
|
||||||
|
|
||||||
userId := c.AppContext.Session().UserId
|
userId := c.AppContext.Session().UserId
|
||||||
|
|
||||||
|
existingScheduledPost, err := c.App.Srv().Store().ScheduledPost().Get(scheduledPostId)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = model.NewAppError("deleteScheduledPost", "app.delete_scheduled_post.get_scheduled_post.error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if existingScheduledPost == nil {
|
||||||
|
c.Err = model.NewAppError("deleteScheduledPost", "app.delete_scheduled_post.existing_scheduled_post.not_exist", nil, "", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if existingScheduledPost.UserId != userId {
|
||||||
|
c.Err = model.NewAppError("deleteScheduledPost", "app.delete_scheduled_post.delete_permission.error", nil, "", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
connectionID := r.Header.Get(model.ConnectionId)
|
connectionID := r.Header.Get(model.ConnectionId)
|
||||||
deletedScheduledPost, appErr := c.App.DeleteScheduledPost(c.AppContext, userId, scheduledPostId, connectionID)
|
deletedScheduledPost, appErr := c.App.DeleteScheduledPost(c.AppContext, userId, scheduledPostId, connectionID)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
|
|||||||
@@ -11,6 +11,88 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestUpdateScheduledPost(t *testing.T) {
|
||||||
|
mainHelper.Parallel(t)
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
|
||||||
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuProfessional))
|
||||||
|
|
||||||
|
t.Run("should not allow updating a scheduled post not belonging to the user", func(t *testing.T) {
|
||||||
|
scheduledPost := &model.ScheduledPost{
|
||||||
|
Draft: model.Draft{
|
||||||
|
CreateAt: model.GetMillis(),
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
ChannelId: th.BasicChannel.Id,
|
||||||
|
Message: "this is a scheduled post",
|
||||||
|
},
|
||||||
|
ScheduledAt: model.GetMillis() + 100000,
|
||||||
|
}
|
||||||
|
createdScheduledPost, _, err := th.Client.CreateScheduledPost(context.Background(), scheduledPost)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, createdScheduledPost)
|
||||||
|
|
||||||
|
originalMessage := createdScheduledPost.Message
|
||||||
|
originalScheduledAt := createdScheduledPost.ScheduledAt
|
||||||
|
|
||||||
|
createdScheduledPost.ScheduledAt = model.GetMillis() + 9999999
|
||||||
|
createdScheduledPost.Message = "Updated Message!!!"
|
||||||
|
|
||||||
|
// Switch to BasicUser2
|
||||||
|
th.LoginBasic2()
|
||||||
|
|
||||||
|
_, resp, err := th.Client.UpdateScheduledPost(context.Background(), createdScheduledPost)
|
||||||
|
require.Error(t, err)
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
|
||||||
|
// Switch back to original user and verify the post wasn't modified
|
||||||
|
th.LoginBasic()
|
||||||
|
|
||||||
|
fetchedPost, err := th.App.Srv().Store().ScheduledPost().Get(createdScheduledPost.Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, fetchedPost)
|
||||||
|
require.Equal(t, originalMessage, fetchedPost.Message)
|
||||||
|
require.Equal(t, originalScheduledAt, fetchedPost.ScheduledAt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteScheduledPost(t *testing.T) {
|
||||||
|
mainHelper.Parallel(t)
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
|
||||||
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuProfessional))
|
||||||
|
|
||||||
|
t.Run("should not allow deleting a scheduled post not belonging to the user", func(t *testing.T) {
|
||||||
|
scheduledPost := &model.ScheduledPost{
|
||||||
|
Draft: model.Draft{
|
||||||
|
CreateAt: model.GetMillis(),
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
ChannelId: th.BasicChannel.Id,
|
||||||
|
Message: "this is a scheduled post",
|
||||||
|
},
|
||||||
|
ScheduledAt: model.GetMillis() + 100000,
|
||||||
|
}
|
||||||
|
createdScheduledPost, _, err := th.Client.CreateScheduledPost(context.Background(), scheduledPost)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, createdScheduledPost)
|
||||||
|
|
||||||
|
// Switch to BasicUser2
|
||||||
|
th.LoginBasic2()
|
||||||
|
|
||||||
|
_, resp, err := th.Client.DeleteScheduledPost(context.Background(), createdScheduledPost.Id)
|
||||||
|
require.Error(t, err)
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
|
||||||
|
// Switch back to original user and verify the post wasn't deleted
|
||||||
|
th.LoginBasic()
|
||||||
|
|
||||||
|
fetchedPost, err := th.App.Srv().Store().ScheduledPost().Get(createdScheduledPost.Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, fetchedPost)
|
||||||
|
require.Equal(t, createdScheduledPost.Id, fetchedPost.Id)
|
||||||
|
require.Equal(t, createdScheduledPost.Message, fetchedPost.Message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateScheduledPost(t *testing.T) {
|
func TestCreateScheduledPost(t *testing.T) {
|
||||||
mainHelper.Parallel(t)
|
mainHelper.Parallel(t)
|
||||||
th := Setup(t).InitBasic()
|
th := Setup(t).InitBasic()
|
||||||
|
|||||||
@@ -725,7 +725,7 @@ func TestAddUserToChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
|
|||||||
assert.Equal(t, channel.Id, history.ChannelId)
|
assert.Equal(t, channel.Id, history.ChannelId)
|
||||||
channelMemberHistoryUserIds = append(channelMemberHistoryUserIds, history.UserId)
|
channelMemberHistoryUserIds = append(channelMemberHistoryUserIds, history.UserId)
|
||||||
}
|
}
|
||||||
assert.Equal(t, groupUserIds, channelMemberHistoryUserIds)
|
assert.ElementsMatch(t, groupUserIds, channelMemberHistoryUserIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsersAndPostsCreateActivityInChannel(t *testing.T) {
|
func TestUsersAndPostsCreateActivityInChannel(t *testing.T) {
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ func (a *App) UpdateScheduledPost(rctx request.CTX, userId string, scheduledPost
|
|||||||
return nil, validationErr
|
return nil, validationErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate the scheduled post belongs to the said user
|
|
||||||
existingScheduledPost, err := a.Srv().Store().ScheduledPost().Get(scheduledPost.Id)
|
existingScheduledPost, err := a.Srv().Store().ScheduledPost().Get(scheduledPost.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, model.NewAppError("app.UpdateScheduledPost", "app.update_scheduled_post.get_scheduled_post.error", map[string]any{"user_id": userId, "scheduled_post_id": scheduledPost.Id}, "", http.StatusInternalServerError).Wrap(err)
|
return nil, model.NewAppError("app.UpdateScheduledPost", "app.update_scheduled_post.get_scheduled_post.error", map[string]any{"user_id": userId, "scheduled_post_id": scheduledPost.Id}, "", http.StatusInternalServerError).Wrap(err)
|
||||||
@@ -73,11 +72,6 @@ func (a *App) UpdateScheduledPost(rctx request.CTX, userId string, scheduledPost
|
|||||||
return nil, model.NewAppError("app.UpdateScheduledPost", "app.update_scheduled_post.existing_scheduled_post.not_exist", map[string]any{"user_id": userId, "scheduled_post_id": scheduledPost.Id}, "", http.StatusNotFound)
|
return nil, model.NewAppError("app.UpdateScheduledPost", "app.update_scheduled_post.existing_scheduled_post.not_exist", map[string]any{"user_id": userId, "scheduled_post_id": scheduledPost.Id}, "", http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
if existingScheduledPost.UserId != userId {
|
|
||||||
return nil, model.NewAppError("app.UpdateScheduledPost", "app.update_scheduled_post.update_permission.error", map[string]any{"user_id": userId, "scheduled_post_id": scheduledPost.Id}, "", http.StatusForbidden)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This step is not required for update but is useful as we want to return the
|
|
||||||
// updated scheduled post. It's better to do this before calling update than after.
|
// updated scheduled post. It's better to do this before calling update than after.
|
||||||
scheduledPost.RestoreNonUpdatableFields(existingScheduledPost)
|
scheduledPost.RestoreNonUpdatableFields(existingScheduledPost)
|
||||||
|
|
||||||
@@ -100,10 +94,6 @@ func (a *App) DeleteScheduledPost(rctx request.CTX, userId, scheduledPostId, con
|
|||||||
return nil, model.NewAppError("app.DeleteScheduledPost", "app.delete_scheduled_post.existing_scheduled_post.not_exist", map[string]any{"user_id": userId, "scheduled_post_id": scheduledPostId}, "", http.StatusNotFound)
|
return nil, model.NewAppError("app.DeleteScheduledPost", "app.delete_scheduled_post.existing_scheduled_post.not_exist", map[string]any{"user_id": userId, "scheduled_post_id": scheduledPostId}, "", http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
if scheduledPost.UserId != userId {
|
|
||||||
return nil, model.NewAppError("app.DeleteScheduledPost", "app.delete_scheduled_post.delete_permission.error", map[string]any{"user_id": userId, "scheduled_post_id": scheduledPostId}, "", http.StatusForbidden)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := a.Srv().Store().ScheduledPost().PermanentlyDeleteScheduledPosts([]string{scheduledPostId}); err != nil {
|
if err := a.Srv().Store().ScheduledPost().PermanentlyDeleteScheduledPosts([]string{scheduledPostId}); err != nil {
|
||||||
return nil, model.NewAppError("app.DeleteScheduledPost", "app.delete_scheduled_post.delete_error", map[string]any{"user_id": userId, "scheduled_post_id": scheduledPostId}, "", http.StatusInternalServerError).Wrap(err)
|
return nil, model.NewAppError("app.DeleteScheduledPost", "app.delete_scheduled_post.delete_error", map[string]any{"user_id": userId, "scheduled_post_id": scheduledPostId}, "", http.StatusInternalServerError).Wrap(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -518,54 +517,6 @@ func TestUpdateScheduledPost(t *testing.T) {
|
|||||||
require.Equal(t, "Updated Message!!!", updatedScheduledPost.Message)
|
require.Equal(t, "Updated Message!!!", updatedScheduledPost.Message)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should ot be allowed to updated a scheduled post not belonging to the user", func(t *testing.T) {
|
|
||||||
// first we'll create a scheduled post
|
|
||||||
userId := model.NewId()
|
|
||||||
|
|
||||||
channel, err := th.GetSqlStore().Channel().Save(th.Context, &model.Channel{
|
|
||||||
Name: model.NewId(),
|
|
||||||
DisplayName: "Channel",
|
|
||||||
Type: model.ChannelTypeOpen,
|
|
||||||
}, 1000)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = th.GetSqlStore().Channel().SaveMember(th.Context, &model.ChannelMember{
|
|
||||||
ChannelId: channel.Id,
|
|
||||||
UserId: userId,
|
|
||||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
||||||
SchemeGuest: false,
|
|
||||||
SchemeUser: true,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = th.GetSqlStore().Channel().Delete(channel.Id, model.GetMillis())
|
|
||||||
_ = th.GetSqlStore().Channel().RemoveMember(th.Context, channel.Id, userId)
|
|
||||||
}()
|
|
||||||
|
|
||||||
scheduledPost := &model.ScheduledPost{
|
|
||||||
Draft: model.Draft{
|
|
||||||
CreateAt: model.GetMillis(),
|
|
||||||
UserId: userId,
|
|
||||||
ChannelId: channel.Id,
|
|
||||||
Message: "this is a scheduled post",
|
|
||||||
},
|
|
||||||
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
||||||
}
|
|
||||||
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
||||||
require.Nil(t, appErr)
|
|
||||||
require.NotNil(t, createdScheduledPost)
|
|
||||||
|
|
||||||
// now we'll try updating it
|
|
||||||
newScheduledAtTime := model.GetMillis() + 9999999
|
|
||||||
createdScheduledPost.ScheduledAt = newScheduledAtTime
|
|
||||||
createdScheduledPost.Message = "Updated Message!!!"
|
|
||||||
updatedScheduledPost, appErr := th.App.UpdateScheduledPost(th.Context, th.BasicUser2.Id, createdScheduledPost, user1ConnID)
|
|
||||||
require.NotNil(t, appErr)
|
|
||||||
require.Equal(t, http.StatusForbidden, appErr.StatusCode)
|
|
||||||
require.Nil(t, updatedScheduledPost)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("should only allow updating limited fields", func(t *testing.T) {
|
t.Run("should only allow updating limited fields", func(t *testing.T) {
|
||||||
// first we'll create a scheduled post
|
// first we'll create a scheduled post
|
||||||
userId := model.NewId()
|
userId := model.NewId()
|
||||||
@@ -716,41 +667,6 @@ func TestDeleteScheduledPost(t *testing.T) {
|
|||||||
require.Nil(t, reFetchedScheduledPost)
|
require.Nil(t, reFetchedScheduledPost)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should not allow deleting someone else's scheduled post", func(t *testing.T) {
|
|
||||||
// first we'll create a scheduled post
|
|
||||||
scheduledPost := &model.ScheduledPost{
|
|
||||||
Draft: model.Draft{
|
|
||||||
CreateAt: model.GetMillis(),
|
|
||||||
UserId: th.BasicUser.Id,
|
|
||||||
ChannelId: th.BasicChannel.Id,
|
|
||||||
Message: "this is a scheduled post",
|
|
||||||
},
|
|
||||||
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
||||||
}
|
|
||||||
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
||||||
require.Nil(t, appErr)
|
|
||||||
require.NotNil(t, createdScheduledPost)
|
|
||||||
|
|
||||||
fetchedScheduledPost, err := th.Server.Store().ScheduledPost().Get(scheduledPost.Id)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, fetchedScheduledPost)
|
|
||||||
require.Equal(t, createdScheduledPost.Id, fetchedScheduledPost.Id)
|
|
||||||
require.Equal(t, createdScheduledPost.Message, fetchedScheduledPost.Message)
|
|
||||||
|
|
||||||
// now we'll delete it
|
|
||||||
var deletedScheduledPost *model.ScheduledPost
|
|
||||||
deletedScheduledPost, appErr = th.App.DeleteScheduledPost(th.Context, th.BasicUser2.Id, scheduledPost.Id, "connection_id")
|
|
||||||
require.NotNil(t, appErr)
|
|
||||||
require.Nil(t, deletedScheduledPost)
|
|
||||||
|
|
||||||
// try to fetch it again
|
|
||||||
reFetchedScheduledPost, err := th.Server.Store().ScheduledPost().Get(scheduledPost.Id)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, reFetchedScheduledPost)
|
|
||||||
require.Equal(t, createdScheduledPost.Id, reFetchedScheduledPost.Id)
|
|
||||||
require.Equal(t, createdScheduledPost.Message, reFetchedScheduledPost.Message)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("should producer error when deleting non existing scheduled post", func(t *testing.T) {
|
t.Run("should producer error when deleting non existing scheduled post", func(t *testing.T) {
|
||||||
var deletedScheduledPost *model.ScheduledPost
|
var deletedScheduledPost *model.ScheduledPost
|
||||||
deletedScheduledPost, appErr := th.App.DeleteScheduledPost(th.Context, th.BasicUser.Id, model.NewId(), "connection_id")
|
deletedScheduledPost, appErr := th.App.DeleteScheduledPost(th.Context, th.BasicUser.Id, model.NewId(), "connection_id")
|
||||||
|
|||||||
@@ -7364,6 +7364,10 @@
|
|||||||
"id": "app.update_error",
|
"id": "app.update_error",
|
||||||
"translation": "update error"
|
"translation": "update error"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "app.update_scheduled_post.convert_to_post.error",
|
||||||
|
"translation": "Unable to convert scheduled post to post format."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "app.update_scheduled_post.existing_scheduled_post.not_exist",
|
"id": "app.update_scheduled_post.existing_scheduled_post.not_exist",
|
||||||
"translation": "Scheduled post does not exist."
|
"translation": "Scheduled post does not exist."
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user