MM-58577 Check remote ownership for posts and reactions (#27317)

* - ensure that posts and reactions can only be added via sync when coming from a remote that the target channel is shared with.
- ensure that posts and reactions are only modified/deleted by the remote that owns them.

* check that reaction belongs to post that belongs to channel that is shared with remote;  check that posts belong to channel shared with remote

* check for correct error type in unit test

* tweak unit test
Этот коммит содержится в:
Doug Lauder
2024-06-11 11:51:00 -04:00
коммит произвёл GitHub
родитель 6f8de3449a
Коммит 594ba6e665
10 изменённых файлов: 276 добавлений и 26 удалений

Просмотреть файл

@@ -198,6 +198,36 @@ func (_m *ReactionStore) GetForPostSince(postId string, since int64, excludeRemo
return r0, r1
}
// GetSingle provides a mock function with given fields: userID, postID, remoteID, emojiName
func (_m *ReactionStore) GetSingle(userID string, postID string, remoteID string, emojiName string) (*model.Reaction, error) {
ret := _m.Called(userID, postID, remoteID, emojiName)
if len(ret) == 0 {
panic("no return value specified for GetSingle")
}
var r0 *model.Reaction
var r1 error
if rf, ok := ret.Get(0).(func(string, string, string, string) (*model.Reaction, error)); ok {
return rf(userID, postID, remoteID, emojiName)
}
if rf, ok := ret.Get(0).(func(string, string, string, string) *model.Reaction); ok {
r0 = rf(userID, postID, remoteID, emojiName)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Reaction)
}
}
if rf, ok := ret.Get(1).(func(string, string, string, string) error); ok {
r1 = rf(userID, postID, remoteID, emojiName)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetUniqueCountForPost provides a mock function with given fields: postId
func (_m *ReactionStore) GetUniqueCountForPost(postId string) (int, error) {
ret := _m.Called(postId)

Просмотреть файл

@@ -31,6 +31,7 @@ func TestReactionStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStor
t.Run("ReactionDeadlock", func(t *testing.T) { testReactionDeadlock(t, rctx, ss) })
t.Run("ExistsOnPost", func(t *testing.T) { testExistsOnPost(t, rctx, ss) })
t.Run("GetUniqueCountForPost", func(t *testing.T) { testGetUniqueCountForPost(t, rctx, ss) })
t.Run("ReactionGetSingle", func(t *testing.T) { testReactionGetSingle(t, rctx, ss) })
}
func testReactionSave(t *testing.T, rctx request.CTX, ss store.Store) {
@@ -938,3 +939,85 @@ func testGetUniqueCountForPost(t *testing.T, rctx request.CTX, ss store.Store) {
require.NoError(t, err)
require.Equal(t, 2, count)
}
func testReactionGetSingle(t *testing.T, rctx request.CTX, ss store.Store) {
var (
testUserID = model.NewId()
testEmojiName = "smile"
testRemoteID = model.NewId()
)
t.Run("get without remoteId", func(t *testing.T) {
post, err := ss.Post().Save(rctx, &model.Post{
ChannelId: model.NewId(),
UserId: testUserID,
})
require.NoError(t, err)
reaction := &model.Reaction{
UserId: testUserID,
PostId: post.Id,
EmojiName: testEmojiName,
}
_, nErr := ss.Reaction().Save(reaction)
require.NoError(t, nErr)
reactionFound, err := ss.Reaction().GetSingle(testUserID, post.Id, "", testEmojiName)
require.NoError(t, err)
assert.Equal(t, testUserID, reactionFound.UserId)
assert.Equal(t, post.Id, reactionFound.PostId)
assert.Equal(t, "", reactionFound.GetRemoteID())
assert.Equal(t, testEmojiName, reactionFound.EmojiName)
})
t.Run("get with remoteId", func(t *testing.T) {
post, err := ss.Post().Save(rctx, &model.Post{
ChannelId: model.NewId(),
UserId: testUserID,
})
require.NoError(t, err)
reaction := &model.Reaction{
UserId: testUserID,
PostId: post.Id,
EmojiName: testEmojiName,
RemoteId: model.NewString(testRemoteID),
}
_, nErr := ss.Reaction().Save(reaction)
require.NoError(t, nErr)
reactionFound, err := ss.Reaction().GetSingle(testUserID, post.Id, testRemoteID, testEmojiName)
require.NoError(t, err)
assert.Equal(t, testUserID, reactionFound.UserId)
assert.Equal(t, post.Id, reactionFound.PostId)
assert.Equal(t, testRemoteID, reactionFound.GetRemoteID())
assert.Equal(t, testEmojiName, reactionFound.EmojiName)
})
t.Run("not found - wrong remoteID", func(t *testing.T) {
post, err := ss.Post().Save(rctx, &model.Post{
ChannelId: model.NewId(),
UserId: testUserID,
})
require.NoError(t, err)
reaction := &model.Reaction{
UserId: testUserID,
PostId: post.Id,
EmojiName: testEmojiName,
RemoteId: model.NewString(testRemoteID),
}
_, nErr := ss.Reaction().Save(reaction)
require.NoError(t, nErr)
reactionFound, err := ss.Reaction().GetSingle(testUserID, post.Id, "bogus-remoteId", testEmojiName)
require.Error(t, err)
assert.Nil(t, reactionFound)
var errNotFound *store.ErrNotFound
assert.ErrorAs(t, err, &errNotFound)
})
}