Add audits for accessing posts without membership (#31266)

* Add audits for accessing posts without membership

* Fix tests

* Use correct audit level

* Address feedback

* Add missing checks all over the app

* Fix lint

* Fix test

* Fix tests

* Fix enterprise test

* Add missing test and docs

* Fix merge

* Fix lint

* Add audit logs on the web socket hook for permalink posts

* Fix lint

* Fix merge conflicts

* Handle all events with "non_channel_member_access" parameter

* Fix lint and tests

* Fix merge

* Fix tests
This commit is contained in:
Daniel Espino García
2026-01-20 10:38:27 +01:00
committed by GitHub
parent b2f93dec1a
commit b5a816a657
89 changed files with 1904 additions and 1113 deletions
@@ -142,7 +142,7 @@ func (_m *MockAppIface) CreateGroupChannel(rctx request.CTX, userIDs []string, c
}
// CreatePost provides a mock function with given fields: rctx, post, channel, flags
func (_m *MockAppIface) CreatePost(rctx request.CTX, post *model.Post, channel *model.Channel, flags model.CreatePostFlags) (*model.Post, *model.AppError) {
func (_m *MockAppIface) CreatePost(rctx request.CTX, post *model.Post, channel *model.Channel, flags model.CreatePostFlags) (*model.Post, bool, *model.AppError) {
ret := _m.Called(rctx, post, channel, flags)
if len(ret) == 0 {
@@ -150,8 +150,9 @@ func (_m *MockAppIface) CreatePost(rctx request.CTX, post *model.Post, channel *
}
var r0 *model.Post
var r1 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, *model.Post, *model.Channel, model.CreatePostFlags) (*model.Post, *model.AppError)); ok {
var r1 bool
var r2 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, *model.Post, *model.Channel, model.CreatePostFlags) (*model.Post, bool, *model.AppError)); ok {
return rf(rctx, post, channel, flags)
}
if rf, ok := ret.Get(0).(func(request.CTX, *model.Post, *model.Channel, model.CreatePostFlags) *model.Post); ok {
@@ -162,15 +163,21 @@ func (_m *MockAppIface) CreatePost(rctx request.CTX, post *model.Post, channel *
}
}
if rf, ok := ret.Get(1).(func(request.CTX, *model.Post, *model.Channel, model.CreatePostFlags) *model.AppError); ok {
if rf, ok := ret.Get(1).(func(request.CTX, *model.Post, *model.Channel, model.CreatePostFlags) bool); ok {
r1 = rf(rctx, post, channel, flags)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
r1 = ret.Get(1).(bool)
}
if rf, ok := ret.Get(2).(func(request.CTX, *model.Post, *model.Channel, model.CreatePostFlags) *model.AppError); ok {
r2 = rf(rctx, post, channel, flags)
} else {
if ret.Get(2) != nil {
r2 = ret.Get(2).(*model.AppError)
}
}
return r0, r1
return r0, r1, r2
}
// CreateUploadSession provides a mock function with given fields: rctx, us
@@ -707,7 +714,7 @@ func (_m *MockAppIface) SaveReactionForPost(rctx request.CTX, reaction *model.Re
}
// SendEphemeralPost provides a mock function with given fields: rctx, userId, post
func (_m *MockAppIface) SendEphemeralPost(rctx request.CTX, userId string, post *model.Post) *model.Post {
func (_m *MockAppIface) SendEphemeralPost(rctx request.CTX, userId string, post *model.Post) (*model.Post, bool) {
ret := _m.Called(rctx, userId, post)
if len(ret) == 0 {
@@ -715,6 +722,10 @@ func (_m *MockAppIface) SendEphemeralPost(rctx request.CTX, userId string, post
}
var r0 *model.Post
var r1 bool
if rf, ok := ret.Get(0).(func(request.CTX, string, *model.Post) (*model.Post, bool)); ok {
return rf(rctx, userId, post)
}
if rf, ok := ret.Get(0).(func(request.CTX, string, *model.Post) *model.Post); ok {
r0 = rf(rctx, userId, post)
} else {
@@ -723,11 +734,17 @@ func (_m *MockAppIface) SendEphemeralPost(rctx request.CTX, userId string, post
}
}
return r0
if rf, ok := ret.Get(1).(func(request.CTX, string, *model.Post) bool); ok {
r1 = rf(rctx, userId, post)
} else {
r1 = ret.Get(1).(bool)
}
return r0, r1
}
// UpdatePost provides a mock function with given fields: rctx, post, updatePostOptions
func (_m *MockAppIface) UpdatePost(rctx request.CTX, post *model.Post, updatePostOptions *model.UpdatePostOptions) (*model.Post, *model.AppError) {
func (_m *MockAppIface) UpdatePost(rctx request.CTX, post *model.Post, updatePostOptions *model.UpdatePostOptions) (*model.Post, bool, *model.AppError) {
ret := _m.Called(rctx, post, updatePostOptions)
if len(ret) == 0 {
@@ -735,8 +752,9 @@ func (_m *MockAppIface) UpdatePost(rctx request.CTX, post *model.Post, updatePos
}
var r0 *model.Post
var r1 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, *model.Post, *model.UpdatePostOptions) (*model.Post, *model.AppError)); ok {
var r1 bool
var r2 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, *model.Post, *model.UpdatePostOptions) (*model.Post, bool, *model.AppError)); ok {
return rf(rctx, post, updatePostOptions)
}
if rf, ok := ret.Get(0).(func(request.CTX, *model.Post, *model.UpdatePostOptions) *model.Post); ok {
@@ -747,15 +765,21 @@ func (_m *MockAppIface) UpdatePost(rctx request.CTX, post *model.Post, updatePos
}
}
if rf, ok := ret.Get(1).(func(request.CTX, *model.Post, *model.UpdatePostOptions) *model.AppError); ok {
if rf, ok := ret.Get(1).(func(request.CTX, *model.Post, *model.UpdatePostOptions) bool); ok {
r1 = rf(rctx, post, updatePostOptions)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
r1 = ret.Get(1).(bool)
}
if rf, ok := ret.Get(2).(func(request.CTX, *model.Post, *model.UpdatePostOptions) *model.AppError); ok {
r2 = rf(rctx, post, updatePostOptions)
} else {
if ret.Get(2) != nil {
r2 = ret.Get(2).(*model.AppError)
}
}
return r0, r1
return r0, r1, r2
}
// UserCanSeeOtherUser provides a mock function with given fields: rctx, userID, otherUserId
@@ -38,7 +38,7 @@ func TestProcessPermalinkToRemote(t *testing.T) {
mockServer.On("Log").Return(logger)
mockApp := scs.app.(*MockAppIface)
mockApp.On("SendEphemeralPost", mock.Anything, "user", mock.AnythingOfType("*model.Post")).Return(&model.Post{}).Times(1)
mockApp.On("SendEphemeralPost", mock.Anything, "user", mock.AnythingOfType("*model.Post")).Return(&model.Post{}, true).Times(1)
defer mockApp.AssertExpectations(t)
t.Run("same channel", func(t *testing.T) {
@@ -54,7 +54,7 @@ type PlatformIface interface {
}
type AppIface interface {
SendEphemeralPost(rctx request.CTX, userId string, post *model.Post) *model.Post
SendEphemeralPost(rctx request.CTX, userId string, post *model.Post) (*model.Post, bool)
CreateChannelWithUser(rctx request.CTX, channel *model.Channel, userId string) (*model.Channel, *model.AppError)
GetOrCreateDirectChannel(rctx request.CTX, userId, otherUserId string, channelOptions ...model.ChannelOption) (*model.Channel, *model.AppError)
CreateGroupChannel(rctx request.CTX, userIDs []string, creatorId string, channelOptions ...model.ChannelOption) (*model.Channel, *model.AppError)
@@ -63,8 +63,8 @@ type AppIface interface {
AddUserToTeamByTeamId(rctx request.CTX, teamId string, user *model.User) *model.AppError
RemoveUserFromChannel(rctx request.CTX, userID string, removerUserId string, channel *model.Channel) *model.AppError
PermanentDeleteChannel(rctx request.CTX, channel *model.Channel) *model.AppError
CreatePost(rctx request.CTX, post *model.Post, channel *model.Channel, flags model.CreatePostFlags) (savedPost *model.Post, err *model.AppError)
UpdatePost(rctx request.CTX, post *model.Post, updatePostOptions *model.UpdatePostOptions) (*model.Post, *model.AppError)
CreatePost(rctx request.CTX, post *model.Post, channel *model.Channel, flags model.CreatePostFlags) (savedPost *model.Post, isMemberForPreviews bool, err *model.AppError)
UpdatePost(rctx request.CTX, post *model.Post, updatePostOptions *model.UpdatePostOptions) (*model.Post, bool, *model.AppError)
DeletePost(rctx request.CTX, postID, deleteByID string) (*model.Post, *model.AppError)
SaveReactionForPost(rctx request.CTX, reaction *model.Reaction) (*model.Reaction, *model.AppError)
DeleteReactionForPost(rctx request.CTX, reaction *model.Reaction) *model.AppError
@@ -315,7 +315,7 @@ func (scs *Service) postUnshareNotification(channelID string, creatorID string,
}
logger := scs.server.Log()
_, appErr := scs.app.CreatePost(request.EmptyContext(logger), post, channel, model.CreatePostFlags{})
_, _, appErr := scs.app.CreatePost(request.EmptyContext(logger), post, channel, model.CreatePostFlags{})
if appErr != nil {
scs.server.Log().Log(
@@ -493,7 +493,7 @@ func (scs *Service) upsertSyncPost(post *model.Post, targetChannel *model.Channe
scs.transformMentionsOnReceive(rctx, post, targetChannel, rc, mentionTransforms)
rpost, appErr = scs.app.CreatePost(rctx, post, targetChannel, model.CreatePostFlags{TriggerWebhooks: true, SetOnline: true})
rpost, _, appErr = scs.app.CreatePost(rctx, post, targetChannel, model.CreatePostFlags{TriggerWebhooks: true, SetOnline: true})
if appErr == nil {
scs.server.Log().Log(mlog.LvlSharedChannelServiceDebug, "Created sync post",
mlog.String("post_id", post.Id),
@@ -527,7 +527,7 @@ func (scs *Service) upsertSyncPost(post *model.Post, targetChannel *model.Channe
}
// First update the basic post
rpost, appErr = scs.app.UpdatePost(rctx, post, nil)
rpost, _, appErr = scs.app.UpdatePost(rctx, post, nil)
if appErr != nil {
rerr := errors.New(appErr.Error())
return nil, rerr