diff --git a/server/channels/api4/channel.go b/server/channels/api4/channel.go index 362e09ded3..df3fa423c5 100644 --- a/server/channels/api4/channel.go +++ b/server/channels/api4/channel.go @@ -167,13 +167,13 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) { switch oldChannel.Type { case model.ChannelTypeOpen: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManagePublicChannelProperties) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManagePublicChannelProperties); !ok { c.SetPermissionError(model.PermissionManagePublicChannelProperties) return } case model.ChannelTypePrivate: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManagePrivateChannelProperties) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManagePrivateChannelProperties); !ok { c.SetPermissionError(model.PermissionManagePrivateChannelProperties) return } @@ -278,14 +278,18 @@ func updateChannelPrivacy(c *Context, w http.ResponseWriter, r *http.Request) { auditRec.AddEventPriorState(channel) - if model.ChannelType(privacy) == model.ChannelTypeOpen && !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionConvertPrivateChannelToPublic) { - c.SetPermissionError(model.PermissionConvertPrivateChannelToPublic) - return + if model.ChannelType(privacy) == model.ChannelTypeOpen { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionConvertPrivateChannelToPublic); !ok { + c.SetPermissionError(model.PermissionConvertPrivateChannelToPublic) + return + } } - if model.ChannelType(privacy) == model.ChannelTypePrivate && !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionConvertPublicChannelToPrivate) { - c.SetPermissionError(model.PermissionConvertPublicChannelToPrivate) - return + if model.ChannelType(privacy) == model.ChannelTypePrivate { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionConvertPublicChannelToPrivate); !ok { + c.SetPermissionError(model.PermissionConvertPublicChannelToPrivate) + return + } } if channel.Name == model.DefaultChannelName && model.ChannelType(privacy) == model.ChannelTypePrivate { @@ -343,13 +347,13 @@ func patchChannel(c *Context, w http.ResponseWriter, r *http.Request) { switch oldChannel.Type { case model.ChannelTypeOpen: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManagePublicChannelProperties) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManagePublicChannelProperties); !ok { c.SetPermissionError(model.PermissionManagePublicChannelProperties) return } case model.ChannelTypePrivate: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManagePrivateChannelProperties) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManagePrivateChannelProperties); !ok { c.SetPermissionError(model.PermissionManagePrivateChannelProperties) return } @@ -631,12 +635,14 @@ func getChannel(c *Context, w http.ResponseWriter, r *http.Request) { } if channel.Type == model.ChannelTypeOpen { - if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionReadPublicChannel) && !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { - c.SetPermissionError(model.PermissionReadPublicChannel) - return + if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionReadPublicChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !ok { + c.SetPermissionError(model.PermissionReadPublicChannel) + return + } } } else { - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -664,7 +670,7 @@ func getChannelUnread(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -689,7 +695,7 @@ func getChannelStats(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -779,7 +785,8 @@ func getPinnedPosts(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = err return } - if !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { + var hasPermission, isMember bool + if hasPermission, isMember = c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel); !hasPermission { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -795,7 +802,7 @@ func getPinnedPosts(c *Context, w http.ResponseWriter, r *http.Request) { } clientPostList := c.App.PreparePostListForClient(c.AppContext, posts) - clientPostList, err = c.App.SanitizePostListMetadataForUser(c.AppContext, clientPostList, c.AppContext.Session().UserId) + clientPostList, isMemberForAllPreviews, err := c.App.SanitizePostListMetadataForUser(c.AppContext, clientPostList, c.AppContext.Session().UserId) if err != nil { c.Err = err return @@ -805,6 +812,14 @@ func getPinnedPosts(c *Context, w http.ResponseWriter, r *http.Request) { if err := clientPostList.EncodeJSON(w); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } + + auditRec := c.MakeAuditRecord("updateChannelMemberRoles", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "channel_id", c.Params.ChannelId) + + if !isMember || !isMemberForAllPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } } func getAllChannels(c *Context, w http.ResponseWriter, r *http.Request) { @@ -1001,7 +1016,7 @@ func getPublicChannelsByIdsForTeam(c *Context, w http.ResponseWriter, r *http.Re if session := c.AppContext.Session(); session.IsGuest() { for _, channel := range channels { - if !c.App.SessionHasPermissionToChannel(c.AppContext, *session, channel.Id, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *session, channel.Id, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -1392,14 +1407,18 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } - if channel.Type == model.ChannelTypeOpen && !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionDeletePublicChannel) { - c.SetPermissionError(model.PermissionDeletePublicChannel) - return + if channel.Type == model.ChannelTypeOpen { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionDeletePublicChannel); !ok { + c.SetPermissionError(model.PermissionDeletePublicChannel) + return + } } - if channel.Type == model.ChannelTypePrivate && !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionDeletePrivateChannel) { - c.SetPermissionError(model.PermissionDeletePrivateChannel) - return + if channel.Type == model.ChannelTypePrivate { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionDeletePrivateChannel); !ok { + c.SetPermissionError(model.PermissionDeletePrivateChannel) + return + } } if c.Params.Permanent { @@ -1436,16 +1455,19 @@ func getChannelByName(c *Context, w http.ResponseWriter, r *http.Request) { } if channel.Type == model.ChannelTypeOpen { - if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionReadPublicChannel) && !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionReadChannel) { - c.SetPermissionError(model.PermissionReadPublicChannel) - return + if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionReadPublicChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionReadChannel); !ok { + c.SetPermissionError(model.PermissionReadPublicChannel) + return + } } } else { // allows team admins to access private channel - if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionManageTeam) && - !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionReadChannel) { - c.Err = model.NewAppError("getChannelByName", "app.channel.get_by_name.missing.app_error", nil, "teamId="+channel.TeamId+", "+"name="+channel.Name+"", http.StatusNotFound) - return + if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionManageTeam) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionReadChannel); !ok { + c.Err = model.NewAppError("getChannelByName", "app.channel.get_by_name.missing.app_error", nil, "teamId="+channel.TeamId+", "+"name="+channel.Name+"", http.StatusNotFound) + return + } } } @@ -1473,7 +1495,7 @@ func getChannelByNameForTeamName(c *Context, w http.ResponseWriter, r *http.Requ return } - channelOk := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionReadChannel) + channelOk, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionReadChannel) if channel.Type == model.ChannelTypeOpen { teamOk := c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionReadPublicChannel) if !teamOk && !channelOk { @@ -1505,7 +1527,7 @@ func getChannelMembers(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -1533,7 +1555,7 @@ func getChannelMembersTimezones(c *Context, w http.ResponseWriter, r *http.Reque return } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -1564,7 +1586,7 @@ func getChannelMembersByIds(c *Context, w http.ResponseWriter, r *http.Request) return } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -1592,7 +1614,7 @@ func getChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -1745,7 +1767,7 @@ func updateChannelMemberRoles(c *Context, w http.ResponseWriter, r *http.Request model.AddEventParameterToAuditRec(auditRec, "props", props) model.AddEventParameterToAuditRec(auditRec, "channel_id", c.Params.ChannelId) - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManageChannelRoles) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManageChannelRoles); !ok { c.SetPermissionError(model.PermissionManageChannelRoles) return } @@ -1777,7 +1799,7 @@ func updateChannelMemberSchemeRoles(c *Context, w http.ResponseWriter, r *http.R model.AddEventParameterToAuditRec(auditRec, "channel_id", c.Params.ChannelId) model.AddEventParameterAuditableToAuditRec(auditRec, "roles", &schemeRoles) - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManageChannelRoles) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManageChannelRoles); !ok { c.SetPermissionError(model.PermissionManageChannelRoles) return } @@ -1888,7 +1910,7 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { // Security check: if the user is a guest, they must have access to the channel // to view its members if c.AppContext.Session().IsGuest() { - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + if hasPermission, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !hasPermission { c.SetPermissionError(model.PermissionReadChannel) return } @@ -1916,13 +1938,13 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { if c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionJoinPublicChannels) { canAddSelf = true } - if c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionManagePublicChannelMembers) { + if hasPermission, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionManagePublicChannelMembers); hasPermission { canAddOthers = true } } if channel.Type == model.ChannelTypePrivate { - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionManagePrivateChannelMembers) { + if hasPermission, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionManagePrivateChannelMembers); !hasPermission { c.SetPermissionError(model.PermissionManagePrivateChannelMembers) return } @@ -2085,14 +2107,18 @@ func removeChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { } if c.Params.UserId != c.AppContext.Session().UserId { - if channel.Type == model.ChannelTypeOpen && !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionManagePublicChannelMembers) { - c.SetPermissionError(model.PermissionManagePublicChannelMembers) - return + if channel.Type == model.ChannelTypeOpen { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionManagePublicChannelMembers); !ok { + c.SetPermissionError(model.PermissionManagePublicChannelMembers) + return + } } - if channel.Type == model.ChannelTypePrivate && !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionManagePrivateChannelMembers) { - c.SetPermissionError(model.PermissionManagePrivateChannelMembers) - return + if channel.Type == model.ChannelTypePrivate { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channel.Id, model.PermissionManagePrivateChannelMembers); !ok { + c.SetPermissionError(model.PermissionManagePrivateChannelMembers) + return + } } } @@ -2234,7 +2260,7 @@ func channelMemberCountsByGroup(c *Context, w http.ResponseWriter, r *http.Reque return } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -2456,7 +2482,7 @@ func getGroupMessageMembersCommonTeams(c *Context, w http.ResponseWriter, r *htt return } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -2530,12 +2556,12 @@ func canEditChannelBanner(c *Context, originalChannel *model.Channel) { switch originalChannel.Type { case model.ChannelTypePrivate: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManagePrivateChannelBanner) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManagePrivateChannelBanner); !ok { c.SetPermissionError(model.PermissionManagePrivateChannelBanner) return } case model.ChannelTypeOpen: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManagePublicChannelBanner) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManagePublicChannelBanner); !ok { c.SetPermissionError(model.PermissionManagePublicChannelBanner) return } @@ -2550,7 +2576,7 @@ func getChannelAccessControlAttributes(c *Context, w http.ResponseWriter, r *htt return } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } diff --git a/server/channels/api4/channel_bookmark.go b/server/channels/api4/channel_bookmark.go index 3203d98266..0f94e2eb6d 100644 --- a/server/channels/api4/channel_bookmark.go +++ b/server/channels/api4/channel_bookmark.go @@ -59,13 +59,13 @@ func createChannelBookmark(c *Context, w http.ResponseWriter, r *http.Request) { switch channel.Type { case model.ChannelTypeOpen: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionAddBookmarkPublicChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionAddBookmarkPublicChannel); !ok { c.SetPermissionError(model.PermissionAddBookmarkPublicChannel) return } case model.ChannelTypePrivate: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionAddBookmarkPrivateChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionAddBookmarkPrivateChannel); !ok { c.SetPermissionError(model.PermissionAddBookmarkPrivateChannel) return } @@ -158,18 +158,23 @@ func updateChannelBookmark(c *Context, w http.ResponseWriter, r *http.Request) { return } + isMember := false switch channel.Type { case model.ChannelTypeOpen: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionEditBookmarkPublicChannel) { + ok, member := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionEditBookmarkPublicChannel) + if !ok { c.SetPermissionError(model.PermissionEditBookmarkPublicChannel) return } + isMember = member case model.ChannelTypePrivate: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionEditBookmarkPrivateChannel) { + ok, member := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionEditBookmarkPrivateChannel) + if !ok { c.SetPermissionError(model.PermissionEditBookmarkPrivateChannel) return } + isMember = member case model.ChannelTypeGroup, model.ChannelTypeDirect: // Any member of DM/GMs but guests can manage channel bookmarks @@ -178,6 +183,7 @@ func updateChannelBookmark(c *Context, w http.ResponseWriter, r *http.Request) { return } + isMember = true user, gAppErr := c.App.GetUser(c.AppContext.Session().UserId) if gAppErr != nil { c.Err = gAppErr @@ -201,6 +207,10 @@ func updateChannelBookmark(c *Context, w http.ResponseWriter, r *http.Request) { return } + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + auditRec.Success() auditRec.AddEventResultState(updateChannelBookmarkResponse) auditRec.AddEventObjectType("updateChannelBookmarkResponse") @@ -250,19 +260,22 @@ func updateChannelBookmarkSortOrder(c *Context, w http.ResponseWriter, r *http.R return } + isMember := false switch channel.Type { case model.ChannelTypeOpen: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionOrderBookmarkPublicChannel) { + ok, member := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionOrderBookmarkPublicChannel) + if !ok { c.SetPermissionError(model.PermissionOrderBookmarkPublicChannel) return } - + isMember = member case model.ChannelTypePrivate: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionOrderBookmarkPrivateChannel) { + ok, member := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionOrderBookmarkPrivateChannel) + if !ok { c.SetPermissionError(model.PermissionOrderBookmarkPrivateChannel) return } - + isMember = member case model.ChannelTypeGroup, model.ChannelTypeDirect: // Any member of DM/GMs but guests can manage channel bookmarks if _, errGet := c.App.GetChannelMember(c.AppContext, channel.Id, c.AppContext.Session().UserId); errGet != nil { @@ -270,6 +283,7 @@ func updateChannelBookmarkSortOrder(c *Context, w http.ResponseWriter, r *http.R return } + isMember = true user, gAppErr := c.App.GetUser(c.AppContext.Session().UserId) if gAppErr != nil { c.Err = gAppErr @@ -292,6 +306,10 @@ func updateChannelBookmarkSortOrder(c *Context, w http.ResponseWriter, r *http.R return } + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + for _, b := range bookmarks { if b.Id == c.Params.ChannelBookmarkId { auditRec.AddEventResultState(b) @@ -335,19 +353,22 @@ func deleteChannelBookmark(c *Context, w http.ResponseWriter, r *http.Request) { return } + isMember := false switch channel.Type { case model.ChannelTypeOpen: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionDeleteBookmarkPublicChannel) { + ok, member := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionDeleteBookmarkPublicChannel) + if !ok { c.SetPermissionError(model.PermissionDeleteBookmarkPublicChannel) return } - + isMember = member case model.ChannelTypePrivate: - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionDeleteBookmarkPrivateChannel) { + ok, member := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionDeleteBookmarkPrivateChannel) + if !ok { c.SetPermissionError(model.PermissionDeleteBookmarkPrivateChannel) return } - + isMember = member case model.ChannelTypeGroup, model.ChannelTypeDirect: // Any member of DM/GMs but guests can manage channel bookmarks if _, errGet := c.App.GetChannelMember(c.AppContext, channel.Id, c.AppContext.Session().UserId); errGet != nil { @@ -355,6 +376,7 @@ func deleteChannelBookmark(c *Context, w http.ResponseWriter, r *http.Request) { return } + isMember = true user, gAppErr := c.App.GetUser(c.AppContext.Session().UserId) if gAppErr != nil { c.Err = gAppErr @@ -390,6 +412,10 @@ func deleteChannelBookmark(c *Context, w http.ResponseWriter, r *http.Request) { return } + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + auditRec.Success() auditRec.AddEventResultState(bookmark) c.LogAudit("bookmark=" + bookmark.DisplayName) @@ -423,7 +449,8 @@ func listChannelBookmarksForChannel(c *Context, w http.ResponseWriter, r *http.R } } - if !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { + hasPermission, isMember := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + if !hasPermission { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -434,6 +461,13 @@ func listChannelBookmarksForChannel(c *Context, w http.ResponseWriter, r *http.R return } + auditRec := c.MakeAuditRecord("listChannelBookmarksForChannel", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "channel_id", c.Params.ChannelId) + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + if err := json.NewEncoder(w).Encode(bookmarks); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } diff --git a/server/channels/api4/command.go b/server/channels/api4/command.go index 3b0e57a491..8b4229e8f0 100644 --- a/server/channels/api4/command.go +++ b/server/channels/api4/command.go @@ -323,7 +323,7 @@ func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) { model.AddEventParameterAuditableToAuditRec(auditRec, "command_args", &commandArgs) // Checks that user is a member of the specified channel, and that they have permission to create a post in it. - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), commandArgs.ChannelId, model.PermissionCreatePost) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), commandArgs.ChannelId, model.PermissionCreatePost); !ok { c.SetPermissionError(model.PermissionCreatePost) return } diff --git a/server/channels/api4/drafts.go b/server/channels/api4/drafts.go index a1a8d02f4e..499e68bfe8 100644 --- a/server/channels/api4/drafts.go +++ b/server/channels/api4/drafts.go @@ -38,7 +38,7 @@ func upsertDraft(c *Context, w http.ResponseWriter, r *http.Request) { hasPermission := false - if c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), draft.ChannelId, model.PermissionCreatePost) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), draft.ChannelId, model.PermissionCreatePost); ok { hasPermission = true } else if channel, err := c.App.GetChannel(c.AppContext, draft.ChannelId); err == nil { // Temporary permission check method until advanced permissions, please do not copy diff --git a/server/channels/api4/file.go b/server/channels/api4/file.go index 10d0aca05d..e080c089c3 100644 --- a/server/channels/api4/file.go +++ b/server/channels/api4/file.go @@ -142,7 +142,7 @@ func uploadFileSimple(c *Context, r *http.Request, timestamp time.Time) *model.F defer c.LogAuditRec(auditRec) model.AddEventParameterToAuditRec(auditRec, "channel_id", c.Params.ChannelId) - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionUploadFile) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionUploadFile); !ok { c.SetPermissionError(model.PermissionUploadFile) return nil } @@ -297,7 +297,7 @@ NextPart: if c.Err != nil { return nil } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionUploadFile) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionUploadFile); !ok { c.SetPermissionError(model.PermissionUploadFile) return nil } @@ -391,7 +391,7 @@ func uploadFileMultipartLegacy(c *Context, mr *multipart.Reader, if c.Err != nil { return nil } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelId, model.PermissionUploadFile) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelId, model.PermissionUploadFile); !ok { c.SetPermissionError(model.PermissionUploadFile) return nil } @@ -488,7 +488,7 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = err return } - perm := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + perm, isMember := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) if info.CreatorId == model.BookmarkFileOwner { if !perm { c.SetPermissionError(model.PermissionReadChannelContent) @@ -510,6 +510,10 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) { auditRec.Success() web.WriteFileResponse(info.Name, info.MimeType, info.Size, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r) + + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } } func getFileThumbnail(c *Context, w http.ResponseWriter, r *http.Request) { @@ -531,7 +535,7 @@ func getFileThumbnail(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = err return } - perm := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + perm, isMember := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) if info.CreatorId == model.BookmarkFileOwner { if !perm { c.SetPermissionError(model.PermissionReadChannelContent) @@ -556,6 +560,13 @@ func getFileThumbnail(c *Context, w http.ResponseWriter, r *http.Request) { defer fileReader.Close() web.WriteFileResponse(info.Name, ThumbnailImageType, 0, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r) + + auditRec := c.MakeAuditRecord("getFileThumbnail", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "file_id", c.Params.FileId) + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } } func getFileLink(c *Context, w http.ResponseWriter, r *http.Request) { @@ -585,7 +596,7 @@ func getFileLink(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = err return } - perm := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + perm, isMember := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) if info.CreatorId == model.BookmarkFileOwner { if !perm { c.SetPermissionError(model.PermissionReadChannelContent) @@ -601,6 +612,10 @@ func getFileLink(c *Context, w http.ResponseWriter, r *http.Request) { return } + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + resp := make(map[string]string) link := c.App.GeneratePublicLink(c.GetSiteURLHeader(), info) resp["link"] = link @@ -631,7 +646,7 @@ func getFilePreview(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = err return } - perm := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + perm, isMember := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) if info.CreatorId == model.BookmarkFileOwner { if !perm { c.SetPermissionError(model.PermissionReadChannelContent) @@ -656,6 +671,13 @@ func getFilePreview(c *Context, w http.ResponseWriter, r *http.Request) { defer fileReader.Close() web.WriteFileResponse(info.Name, PreviewImageType, 0, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r) + + auditRec := c.MakeAuditRecord("getFilePreview", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "file_id", c.Params.FileId) + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } } func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) { @@ -676,7 +698,7 @@ func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = err return } - perm := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + perm, isMember := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) if info.CreatorId == model.BookmarkFileOwner { if !perm { c.SetPermissionError(model.PermissionReadChannelContent) @@ -691,6 +713,14 @@ func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) { if err := json.NewEncoder(w).Encode(info); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } + + auditRec := c.MakeAuditRecord("getFileInfo", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "file_id", c.Params.FileId) + + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } } func getPublicFile(c *Context, w http.ResponseWriter, r *http.Request) { @@ -795,7 +825,7 @@ func searchFiles(c *Context, w http.ResponseWriter, r *http.Request, teamID stri startTime := time.Now() - results, err := c.App.SearchFilesInTeamForUser(c.AppContext, terms, c.AppContext.Session().UserId, teamID, isOrSearch, includeDeletedChannels, timeZoneOffset, page, perPage) + results, allFilesHaveMembership, err := c.App.SearchFilesInTeamForUser(c.AppContext, terms, c.AppContext.Session().UserId, teamID, isOrSearch, includeDeletedChannels, timeZoneOffset, page, perPage) elapsedTime := float64(time.Since(startTime)) / float64(time.Second) metrics := c.App.Metrics() @@ -813,6 +843,16 @@ func searchFiles(c *Context, w http.ResponseWriter, r *http.Request, teamID stri if err := json.NewEncoder(w).Encode(results); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } + + auditRec := c.MakeAuditRecord("searchFiles", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterAuditableToAuditRec(auditRec, "search_params", params) + + if !allFilesHaveMembership { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + + auditRec.Success() } func setInaccessibleFileHeader(w http.ResponseWriter, appErr *model.AppError) { diff --git a/server/channels/api4/group.go b/server/channels/api4/group.go index d3bb0c4813..f3915ec445 100644 --- a/server/channels/api4/group.go +++ b/server/channels/api4/group.go @@ -702,7 +702,7 @@ func verifyLinkUnlinkPermission(c *Context, syncableType model.GroupSyncableType permission = model.PermissionManagePublicChannelMembers } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), syncableID, permission) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), syncableID, permission); !ok { return model.MakePermissionError(c.AppContext.Session(), []*model.Permission{permission}) } } @@ -936,7 +936,7 @@ func getGroupsByChannelCommon(c *Context, r *http.Request) ([]byte, *model.AppEr } else { permission = model.PermissionReadPublicChannelGroups } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, permission) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, permission); !ok { return nil, model.MakePermissionError(c.AppContext.Session(), []*model.Permission{permission}) } @@ -1102,7 +1102,7 @@ func getGroups(c *Context, w http.ResponseWriter, r *http.Request) { } else { permission = model.PermissionManagePublicChannelMembers } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), NotAssociatedToChannelID, permission) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), NotAssociatedToChannelID, permission); !ok { c.SetPermissionError(permission) return } @@ -1121,7 +1121,7 @@ func getGroups(c *Context, w http.ResponseWriter, r *http.Request) { } else { permission = model.PermissionManagePublicChannelMembers } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), ChannelIDForMemberCount, permission) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), ChannelIDForMemberCount, permission); !ok { c.SetPermissionError(permission) return } diff --git a/server/channels/api4/integration_action.go b/server/channels/api4/integration_action.go index 7076009b81..b7e825132d 100644 --- a/server/channels/api4/integration_action.go +++ b/server/channels/api4/integration_action.go @@ -49,12 +49,12 @@ func doPostAction(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = err return } - if !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { + if ok, _ := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel); !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } } else { - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PermissionReadChannelContent) { + if ok, _ := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), c.Params.PostId); !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -118,7 +118,7 @@ func submitDialog(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = err return } - if !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { + if ok, _ := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel); !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } diff --git a/server/channels/api4/post.go b/server/channels/api4/post.go index 96d3437b45..586f813e39 100644 --- a/server/channels/api4/post.go +++ b/server/channels/api4/post.go @@ -104,7 +104,7 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) { } } - rp, err := c.App.CreatePostAsUser(c.AppContext, c.App.PostWithProxyRemovedFromImageURLs(&post), c.AppContext.Session().Id, setOnlineBool) + rp, isMemberForPreviews, err := c.App.CreatePostAsUser(c.AppContext, c.App.PostWithProxyRemovedFromImageURLs(&post), c.AppContext.Session().Id, setOnlineBool) if err != nil { c.Err = err return @@ -113,6 +113,14 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) { auditRec.AddEventResultState(rp) auditRec.AddEventObjectType("post") + if !isMemberForPreviews { + previewPost := rp.GetPreviewPost() + if previewPost != nil { + model.AddEventParameterToAuditRec(auditRec, "preview_post_id", previewPost.Post.Id) + } + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + if setOnlineBool { c.App.SetStatusOnline(c.AppContext.Session().UserId, false) } @@ -155,12 +163,13 @@ func createEphemeralPost(c *Context, w http.ResponseWriter, r *http.Request) { return } - rp := c.App.SendEphemeralPost(c.AppContext, ephRequest.UserID, c.App.PostWithProxyRemovedFromImageURLs(ephRequest.Post)) + // We prepare again the post here, so we can ignore the isMemberForPreviews return value from SendEphemeralPost + rp, _ := c.App.SendEphemeralPost(c.AppContext, ephRequest.UserID, c.App.PostWithProxyRemovedFromImageURLs(ephRequest.Post)) w.WriteHeader(http.StatusCreated) rp = model.AddPostActionCookies(rp, c.App.PostActionCookieSecret()) rp = c.App.PreparePostForClientWithEmbedsAndImages(c.AppContext, rp, true, false, true) - rp, err := c.App.SanitizePostMetadataForUser(c.AppContext, rp, c.AppContext.Session().UserId) + rp, isMemberForPreviews, err := c.App.SanitizePostMetadataForUser(c.AppContext, rp, c.AppContext.Session().UserId) if err != nil { c.Err = err return @@ -168,6 +177,19 @@ func createEphemeralPost(c *Context, w http.ResponseWriter, r *http.Request) { if err := rp.EncodeJSON(w); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } + + auditRec := c.MakeAuditRecord("createEphemeralPost", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "post_id", rp.Id) + + if !isMemberForPreviews { + previewPost := rp.GetPreviewPost() + if previewPost != nil { + model.AddEventParameterToAuditRec(auditRec, "preview_post_id", previewPost.Post.Id) + } + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + auditRec.Success() } func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { @@ -216,7 +238,8 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = err return } - if !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { + hasPermission, isMember := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + if !hasPermission { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -275,7 +298,7 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { c.App.AddCursorIdsForPostList(list, afterPost, beforePost, since, page, perPage, collapsedThreads) clientPostList := c.App.PreparePostListForClient(c.AppContext, list) - clientPostList, err = c.App.SanitizePostListMetadataForUser(c.AppContext, clientPostList, c.AppContext.Session().UserId) + clientPostList, isMemberForAllPreviews, err := c.App.SanitizePostListMetadataForUser(c.AppContext, clientPostList, c.AppContext.Session().UserId) if err != nil { c.Err = err return @@ -284,6 +307,16 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { if err := clientPostList.EncodeJSON(w); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } + + auditRec := c.MakeAuditRecord("getPostsForChannel", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "channel_id", channelId) + if !isMember || !isMemberForAllPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + if !isMemberForAllPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access_on_previews", true) + } + } } func getPostsForChannelAroundLastUnread(c *Context, w http.ResponseWriter, r *http.Request) { @@ -304,7 +337,8 @@ func getPostsForChannelAroundLastUnread(c *Context, w http.ResponseWriter, r *ht c.Err = err return } - if !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { + hasPermission, isMember := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + if !hasPermission { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -343,7 +377,7 @@ func getPostsForChannelAroundLastUnread(c *Context, w http.ResponseWriter, r *ht postList.PrevPostId = c.App.GetPrevPostIdFromPostList(postList, collapsedThreads) clientPostList := c.App.PreparePostListForClient(c.AppContext, postList) - clientPostList, err = c.App.SanitizePostListMetadataForUser(c.AppContext, clientPostList, c.AppContext.Session().UserId) + clientPostList, isMemberForAllPreviews, err := c.App.SanitizePostListMetadataForUser(c.AppContext, clientPostList, c.AppContext.Session().UserId) if err != nil { c.Err = err return @@ -355,6 +389,17 @@ func getPostsForChannelAroundLastUnread(c *Context, w http.ResponseWriter, r *ht if err := clientPostList.EncodeJSON(w); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } + + auditRec := c.MakeAuditRecord("getPostsForChannelAroundLastUnread", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "channel_id", channelId) + + if !isMember || !isMemberForAllPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + if !isMemberForAllPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access_on_previews", true) + } + } } func getFlaggedPostsForUser(c *Context, w http.ResponseWriter, r *http.Request) { @@ -402,6 +447,7 @@ func getFlaggedPostsForUser(c *Context, w http.ResponseWriter, r *http.Request) pl := model.NewPostList() channelReadPermission := make(map[string]bool) + isMemberForAllPosts := true for _, post := range posts.Posts { allowed, ok := channelReadPermission[post.ChannelId] @@ -413,8 +459,11 @@ func getFlaggedPostsForUser(c *Context, w http.ResponseWriter, r *http.Request) if !ok { continue } - if c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { + + hasPermission, isMember := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + if hasPermission { allowed = true + isMemberForAllPosts = isMemberForAllPosts && isMember } channelReadPermission[post.ChannelId] = allowed @@ -430,11 +479,23 @@ func getFlaggedPostsForUser(c *Context, w http.ResponseWriter, r *http.Request) pl.SortByCreateAt() clientPostList := c.App.PreparePostListForClient(c.AppContext, pl) - clientPostList, err = c.App.SanitizePostListMetadataForUser(c.AppContext, clientPostList, c.AppContext.Session().UserId) + clientPostList, isMemberForAllPreviews, err := c.App.SanitizePostListMetadataForUser(c.AppContext, clientPostList, c.AppContext.Session().UserId) if err != nil { c.Err = err return } + + auditRec := c.MakeAuditRecord("getFlaggedPosts", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "channel_id", channelId) + + if !isMemberForAllPosts || !isMemberForAllPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + if !isMemberForAllPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access_on_previews", true) + } + } + if err := clientPostList.EncodeJSON(w); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } @@ -453,7 +514,7 @@ func getPost(c *Context, w http.ResponseWriter, r *http.Request) { return } - post, err := c.App.GetPostIfAuthorized(c.AppContext, c.Params.PostId, c.AppContext.Session(), includeDeleted) + post, err, isMember := c.App.GetPostIfAuthorized(c.AppContext, c.Params.PostId, c.AppContext.Session(), includeDeleted) if err != nil { c.Err = err @@ -466,7 +527,7 @@ func getPost(c *Context, w http.ResponseWriter, r *http.Request) { } post = c.App.PreparePostForClientWithEmbedsAndImages(c.AppContext, post, false, false, true) - post, err = c.App.SanitizePostMetadataForUser(c.AppContext, post, c.AppContext.Session().UserId) + post, previewIsMember, err := c.App.SanitizePostMetadataForUser(c.AppContext, post, c.AppContext.Session().UserId) if err != nil { c.Err = err return @@ -480,6 +541,20 @@ func getPost(c *Context, w http.ResponseWriter, r *http.Request) { if err := post.EncodeJSON(w); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } + + auditRec := c.MakeAuditRecord("getPost", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "post_id", c.Params.PostId) + + if !isMember || !previewIsMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + if !previewIsMember { + previewPost := post.GetPreviewPost() + if previewPost != nil { + model.AddEventParameterToAuditRec(auditRec, "preview_post_id", previewPost.Post.Id) + } + } + } } // getPostsByIds also sets a header to indicate, if posts were truncated as per the cloud plan's limit. @@ -519,16 +594,20 @@ func getPostsByIds(c *Context, w http.ResponseWriter, r *http.Request) { } var posts = []*model.Post{} + isMemberForAllPosts := true for _, post := range postsList { channel, ok := channelMap[post.ChannelId] if !ok { continue } - if !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { + hasPermission, isMemberForCurrentPost := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + if !hasPermission { continue } + isMemberForAllPosts = isMemberForAllPosts && isMemberForCurrentPost + post = c.App.PreparePostForClient(c.AppContext, post, false, false, true) post.StripActionIntegrations() posts = append(posts, post) @@ -539,6 +618,14 @@ func getPostsByIds(c *Context, w http.ResponseWriter, r *http.Request) { if err := json.NewEncoder(w).Encode(posts); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } + + auditRec := c.MakeAuditRecord("getPostsByIds", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "post_ids", postIDs) + + if !isMemberForAllPosts { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } } func getEditHistoryForPost(c *Context, w http.ResponseWriter, r *http.Request) { @@ -553,7 +640,8 @@ func getEditHistoryForPost(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), originalPost.ChannelId, model.PermissionEditPost) { + ok, isMember := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), originalPost.ChannelId, model.PermissionEditPost) + if !ok { c.SetPermissionError(model.PermissionEditPost) return } @@ -569,6 +657,14 @@ func getEditHistoryForPost(c *Context, w http.ResponseWriter, r *http.Request) { return } + auditRec := c.MakeAuditRecord("getEditHistoryForPost", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "post_id", c.Params.PostId) + + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + if err := json.NewEncoder(w).Encode(postsList); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } @@ -608,12 +704,12 @@ func deletePost(c *Context, w http.ResponseWriter, _ *http.Request) { auditRec.AddEventObjectType("post") if c.AppContext.Session().UserId == post.UserId { - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), post.ChannelId, model.PermissionDeletePost) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), post.ChannelId, model.PermissionDeletePost); !ok { c.SetPermissionError(model.PermissionDeletePost) return } } else { - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), post.ChannelId, model.PermissionDeleteOthersPosts) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), post.ChannelId, model.PermissionDeleteOthersPosts); !ok { c.SetPermissionError(model.PermissionDeleteOthersPosts) return } @@ -739,7 +835,8 @@ func getPostThread(c *Context, w http.ResponseWriter, r *http.Request) { return } - if _, err = c.App.GetPostIfAuthorized(c.AppContext, post.Id, c.AppContext.Session(), false); err != nil { + var isMember bool + if _, err, isMember = c.App.GetPostIfAuthorized(c.AppContext, post.Id, c.AppContext.Session(), false); err != nil { c.Err = err return } @@ -749,7 +846,7 @@ func getPostThread(c *Context, w http.ResponseWriter, r *http.Request) { } clientPostList := c.App.PreparePostListForClient(c.AppContext, list) - clientPostList, err = c.App.SanitizePostListMetadataForUser(c.AppContext, clientPostList, c.AppContext.Session().UserId) + clientPostList, isMemberForAllPreviews, err := c.App.SanitizePostListMetadataForUser(c.AppContext, clientPostList, c.AppContext.Session().UserId) if err != nil { c.Err = err return @@ -760,6 +857,17 @@ func getPostThread(c *Context, w http.ResponseWriter, r *http.Request) { if err := clientPostList.EncodeJSON(w); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } + + auditRec := c.MakeAuditRecord("getPostThread", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "post_id", c.Params.PostId) + + if !isMember || !isMemberForAllPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + if !isMemberForAllPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access_on_previews", true) + } + } } func searchPostsInTeam(c *Context, w http.ResponseWriter, r *http.Request) { @@ -824,7 +932,7 @@ func searchPosts(c *Context, w http.ResponseWriter, r *http.Request, teamId stri startTime := time.Now() - results, err := c.App.SearchPostsForUser(c.AppContext, terms, c.AppContext.Session().UserId, teamId, isOrSearch, includeDeletedChannels, timeZoneOffset, page, perPage) + results, allPostHaveMembership, err := c.App.SearchPostsForUser(c.AppContext, terms, c.AppContext.Session().UserId, teamId, isOrSearch, includeDeletedChannels, timeZoneOffset, page, perPage) elapsedTime := float64(time.Since(startTime)) / float64(time.Second) metrics := c.App.Metrics() @@ -839,12 +947,19 @@ func searchPosts(c *Context, w http.ResponseWriter, r *http.Request, teamId stri } clientPostList := c.App.PreparePostListForClient(c.AppContext, results.PostList) - clientPostList, err = c.App.SanitizePostListMetadataForUser(c.AppContext, clientPostList, c.AppContext.Session().UserId) + clientPostList, isMemberForAllPreviews, err := c.App.SanitizePostListMetadataForUser(c.AppContext, clientPostList, c.AppContext.Session().UserId) if err != nil { c.Err = err return } + if !allPostHaveMembership || !isMemberForAllPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + if !isMemberForAllPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access_on_previews", true) + } + } + results = model.MakePostSearchResults(clientPostList, results.Matches) model.AddEventParameterAuditableToAuditRec(auditRec, "search_results", results) auditRec.Success() @@ -888,7 +1003,8 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), originalPost.ChannelId, model.PermissionEditPost) { + ok, isMember := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), originalPost.ChannelId, model.PermissionEditPost) + if !ok { c.SetPermissionError(model.PermissionEditPost) return } @@ -903,7 +1019,8 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { } if c.AppContext.Session().UserId != originalPost.UserId { - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), originalPost.ChannelId, model.PermissionEditOthersPosts) { + // 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 { c.SetPermissionError(model.PermissionEditOthersPosts) return } @@ -916,12 +1033,22 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - rpost, err := c.App.UpdatePost(c.AppContext, c.App.PostWithProxyRemovedFromImageURLs(&post), &model.UpdatePostOptions{SafeUpdate: false}) + rpost, isMemberForPreviews, err := c.App.UpdatePost(c.AppContext, c.App.PostWithProxyRemovedFromImageURLs(&post), &model.UpdatePostOptions{SafeUpdate: false}) if err != nil { c.Err = err return } + if !isMember || !isMemberForPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + if !isMemberForPreviews { + previewPost := rpost.GetPreviewPost() + if previewPost != nil { + model.AddEventParameterToAuditRec(auditRec, "preview_post_id", previewPost.Post.Id) + } + } + } + auditRec.Success() auditRec.AddEventResultState(rpost) @@ -954,17 +1081,21 @@ func patchPost(c *Context, w http.ResponseWriter, r *http.Request) { } } - postPatchChecks(c, auditRec, post.Message) + isMember := postPatchChecks(c, auditRec, post.Message) if c.Err != nil { return } - patchedPost, 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 { c.Err = err return } + if !isMember || !isMemberForPReviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + auditRec.Success() auditRec.AddEventResultState(patchedPost) @@ -973,11 +1104,11 @@ func patchPost(c *Context, w http.ResponseWriter, r *http.Request) { } } -func postPatchChecks(c *Context, auditRec *model.AuditRecord, message *string) { +func postPatchChecks(c *Context, auditRec *model.AuditRecord, message *string) bool { originalPost, err := c.App.GetSinglePost(c.AppContext, c.Params.PostId, false) if err != nil { c.SetPermissionError(model.PermissionEditPost) - return + return false } auditRec.AddEventPriorState(originalPost) auditRec.AddEventObjectType("post") @@ -990,15 +1121,18 @@ func postPatchChecks(c *Context, auditRec *model.AuditRecord, message *string) { permission = model.PermissionEditOthersPosts } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), originalPost.ChannelId, permission) { + ok, isMember := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), originalPost.ChannelId, permission) + if !ok { c.SetPermissionError(permission) - return + return false } if *c.App.Config().ServiceSettings.PostEditTimeLimit != -1 && model.GetMillis() > originalPost.CreateAt+int64(*c.App.Config().ServiceSettings.PostEditTimeLimit*1000) && message != nil { c.Err = model.NewAppError("patchPost", "api.post.update_post.permissions_time_limit.app_error", map[string]any{"timeLimit": *c.App.Config().ServiceSettings.PostEditTimeLimit}, "", http.StatusBadRequest) - return + return isMember } + + return isMember } func setPostUnread(c *Context, w http.ResponseWriter, r *http.Request) { @@ -1014,7 +1148,7 @@ func setPostUnread(c *Context, w http.ResponseWriter, r *http.Request) { c.SetPermissionError(model.PermissionEditOtherUsers) return } - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PermissionReadChannelContent) { + if ok, _ := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), c.Params.PostId); !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -1039,7 +1173,7 @@ func setPostReminder(c *Context, w http.ResponseWriter, r *http.Request) { c.SetPermissionError(model.PermissionEditOtherUsers) return } - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PermissionReadChannelContent) { + if ok, _ := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), c.Params.PostId); !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -1082,7 +1216,8 @@ func saveIsPinnedPost(c *Context, w http.ResponseWriter, isPinned bool) { c.Err = err return } - if !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { + ok, isMember := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + if !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -1090,11 +1225,22 @@ func saveIsPinnedPost(c *Context, w http.ResponseWriter, isPinned bool) { patch := &model.PostPatch{} patch.IsPinned = model.NewPointer(isPinned) - patchedPost, err := c.App.PatchPost(c.AppContext, c.Params.PostId, patch, nil) + patchedPost, isMemberForPreviews, err := c.App.PatchPost(c.AppContext, c.Params.PostId, patch, nil) if err != nil { c.Err = err return } + + if !isMember || !isMemberForPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + if !isMemberForPreviews { + previewPost := patchedPost.GetPreviewPost() + if previewPost != nil { + model.AddEventParameterToAuditRec(auditRec, "preview_post_id", previewPost.Post.Id) + } + } + } + auditRec.AddEventResultState(patchedPost) auditRec.Success() @@ -1126,7 +1272,7 @@ func acknowledgePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PermissionReadChannelContent) { + if ok, _ := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), c.Params.PostId); !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -1165,7 +1311,7 @@ func unacknowledgePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PermissionReadChannelContent) { + if ok, _ := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), c.Params.PostId); !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -1246,7 +1392,7 @@ func moveThread(c *Context, w http.ResponseWriter, r *http.Request) { return } - sourcePost, err := c.App.GetPostIfAuthorized(c.AppContext, c.Params.PostId, c.AppContext.Session(), false) + sourcePost, err, _ := c.App.GetPostIfAuthorized(c.AppContext, c.Params.PostId, c.AppContext.Session(), false) if err != nil { c.Err = err if err.Id == "app.post.cloud.get.app_error" { @@ -1273,7 +1419,8 @@ func getFileInfosForPost(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PermissionReadChannelContent) { + ok, isMember := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), c.Params.PostId) + if !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -1300,6 +1447,14 @@ func getFileInfosForPost(c *Context, w http.ResponseWriter, r *http.Request) { return } + auditRec := c.MakeAuditRecord("getFileInfosForPost", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "post_id", c.Params.PostId) + + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + w.Header().Set("Cache-Control", "max-age=2592000, private") w.Header().Set(model.HeaderEtagServer, model.GetEtagForFileInfos(infos)) if _, err := w.Write(js); err != nil { @@ -1360,17 +1515,27 @@ func restorePostVersion(c *Context, w http.ResponseWriter, r *http.Request) { return } - postPatchChecks(c, auditRec, &toRestorePost.Message) + isMember := postPatchChecks(c, auditRec, &toRestorePost.Message) if c.Err != nil { return } - updatedPost, appErr := c.App.RestorePostVersion(c.AppContext, c.AppContext.Session().UserId, c.Params.PostId, restoreVersionId) + updatedPost, isMemberForPreview, appErr := c.App.RestorePostVersion(c.AppContext, c.AppContext.Session().UserId, c.Params.PostId, restoreVersionId) if appErr != nil { c.Err = appErr return } + if !isMember || !isMemberForPreview { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + if !isMemberForPreview { + previewPost := updatedPost.GetPreviewPost() + if previewPost != nil { + model.AddEventParameterToAuditRec(auditRec, "preview_post_id", previewPost.Post.Id) + } + } + } + auditRec.Success() auditRec.AddEventResultState(updatedPost) diff --git a/server/channels/api4/post_test.go b/server/channels/api4/post_test.go index 29eb787d53..58d22e1af6 100644 --- a/server/channels/api4/post_test.go +++ b/server/channels/api4/post_test.go @@ -1375,7 +1375,7 @@ func TestUpdatePost(t *testing.T) { fileIds[i] = fileResp.FileInfos[0].Id } - rpost, appErr := th.App.CreatePost(th.Context, &model.Post{ + rpost, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: channel.Id, Message: "zz" + model.NewId() + "a", @@ -1408,7 +1408,7 @@ func TestUpdatePost(t *testing.T) { t.Run("join/leave post", func(t *testing.T) { var rpost2 *model.Post - rpost2, appErr = th.App.CreatePost(th.Context, &model.Post{ + rpost2, _, appErr = th.App.CreatePost(th.Context, &model.Post{ ChannelId: channel.Id, Message: "zz" + model.NewId() + "a", Type: model.PostTypeJoinLeave, @@ -1426,7 +1426,7 @@ func TestUpdatePost(t *testing.T) { CheckBadRequestStatus(t, resp) }) - rpost3, appErr := th.App.CreatePost(th.Context, &model.Post{ + rpost3, _, appErr := th.App.CreatePost(th.Context, &model.Post{ ChannelId: channel.Id, Message: "zz" + model.NewId() + "a", UserId: th.BasicUser.Id, @@ -1458,7 +1458,7 @@ func TestUpdatePost(t *testing.T) { *cfg.ServiceSettings.PostEditTimeLimit = -1 }) - rpost4, appErr := th.App.CreatePost(th.Context, &model.Post{ + rpost4, _, appErr := th.App.CreatePost(th.Context, &model.Post{ ChannelId: channel.Id, Message: "zz" + model.NewId() + "a", UserId: th.BasicUser.Id, @@ -1538,7 +1538,7 @@ func TestUpdatePost(t *testing.T) { fileInfo := fileResponse.FileInfos[0] // create new post - post, appErr := th.App.CreatePost(th.Context, &model.Post{ + post, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: channel.Id, Message: "zz" + model.NewId() + "a", @@ -1574,7 +1574,7 @@ func TestUpdatePost(t *testing.T) { fileInfo := fileResponse.FileInfos[0] // create new post - post, appErr := th.App.CreatePost(th.Context, &model.Post{ + post, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: channel.Id, Message: "zz" + model.NewId() + "a", @@ -1612,7 +1612,7 @@ func TestUpdatePost(t *testing.T) { fileInfo := fileResponse.FileInfos[0] // create new post - post, appErr := th.App.CreatePost(th.Context, &model.Post{ + post, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: channel.Id, Message: "zz" + model.NewId() + "a", @@ -1658,7 +1658,7 @@ func TestUpdatePost(t *testing.T) { fileInfo2 := fileResponse2.FileInfos[0] // create new post - post, appErr := th.App.CreatePost(th.Context, &model.Post{ + post, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: channel.Id, Message: "zz" + model.NewId() + "a", @@ -4322,13 +4322,13 @@ func TestSetChannelUnread(t *testing.T) { t.Run("Unread on a direct channel in a thread", func(t *testing.T) { dc := th.CreateDmChannel(th.CreateUser()) - rootPost, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: u1.Id, CreateAt: now, ChannelId: dc.Id, Message: "root"}, dc, model.CreatePostFlags{}) + rootPost, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: u1.Id, CreateAt: now, ChannelId: dc.Id, Message: "root"}, dc, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: u1.Id, CreateAt: now + 10, ChannelId: dc.Id, Message: "reply 1"}, dc, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: u1.Id, CreateAt: now + 10, ChannelId: dc.Id, Message: "reply 1"}, dc, model.CreatePostFlags{}) require.Nil(t, appErr) - reply2, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: u1.Id, CreateAt: now + 20, ChannelId: dc.Id, Message: "reply 2"}, dc, model.CreatePostFlags{}) + reply2, _, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: u1.Id, CreateAt: now + 20, ChannelId: dc.Id, Message: "reply 2"}, dc, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: u1.Id, CreateAt: now + 30, ChannelId: dc.Id, Message: "reply 3"}, dc, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: u1.Id, CreateAt: now + 30, ChannelId: dc.Id, Message: "reply 3"}, dc, model.CreatePostFlags{}) require.Nil(t, appErr) // Ensure that post have been read @@ -4428,19 +4428,19 @@ func TestSetPostUnreadWithoutCollapsedThreads(t *testing.T) { // user1: a root post // user2: Another root mention @u1 user1Mention := " @" + th.BasicUser.Username - rootPost1, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "first root mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) + rootPost1, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "first root mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hello"}, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hello"}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - replyPost1, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) + replyPost1, _, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another reply"}, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another reply"}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "a root post"}, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "a root post"}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another root mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another root mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) t.Run("Mark reply post as unread", func(t *testing.T) { @@ -4546,7 +4546,7 @@ func TestGetEditHistoryForPost(t *testing.T) { UserId: th.BasicUser.Id, } - rpost, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) time.Sleep(1 * time.Millisecond) @@ -4617,7 +4617,7 @@ func TestGetEditHistoryForPost(t *testing.T) { FileIds: []string{fileInfo1.Id, fileInfo2.Id}, } - createdPost, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + createdPost, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) require.Contains(t, createdPost.FileIds, fileInfo1.Id) require.Contains(t, createdPost.FileIds, fileInfo2.Id) @@ -4774,7 +4774,7 @@ func TestCreatePostNotificationsWithCRT(t *testing.T) { require.NoError(t, err) // post a reply on the thread - _, appErr := th.App.CreatePostAsUser(th.Context, tc.post, th.Context.Session().Id, false) + _, _, appErr := th.App.CreatePostAsUser(th.Context, tc.post, th.Context.Session().Id, false) require.Nil(t, appErr) var caught bool diff --git a/server/channels/api4/post_utils.go b/server/channels/api4/post_utils.go index d135a0fbfd..08d03578a5 100644 --- a/server/channels/api4/post_utils.go +++ b/server/channels/api4/post_utils.go @@ -10,7 +10,7 @@ import ( func userCreatePostPermissionCheckWithContext(c *Context, channelId string) { hasPermission := false - if c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelId, model.PermissionCreatePost) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelId, model.PermissionCreatePost); ok { hasPermission = true } else if channel, err := c.App.GetChannel(c.AppContext, channelId); err == nil { // Temporary permission check method until advanced permissions, please do not copy diff --git a/server/channels/api4/preference.go b/server/channels/api4/preference.go index 8902140925..59b6128cdd 100644 --- a/server/channels/api4/preference.go +++ b/server/channels/api4/preference.go @@ -131,7 +131,7 @@ func updatePreferences(c *Context, w http.ResponseWriter, r *http.Request) { } } - if !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { + if ok, _ := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel); !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } diff --git a/server/channels/api4/reaction.go b/server/channels/api4/reaction.go index a9fb7c0e7d..cb67d08a97 100644 --- a/server/channels/api4/reaction.go +++ b/server/channels/api4/reaction.go @@ -57,7 +57,7 @@ func getReactions(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.PostId, model.PermissionReadChannelContent) { + if ok, _ := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), c.Params.PostId); !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -117,7 +117,7 @@ func getBulkReactions(c *Context, w http.ResponseWriter, r *http.Request) { return } for _, postId := range postIds { - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), postId, model.PermissionReadChannelContent) { + if ok, _ := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), postId); !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } diff --git a/server/channels/api4/shared_channel.go b/server/channels/api4/shared_channel.go index 7fe8830092..e118d8077a 100644 --- a/server/channels/api4/shared_channel.go +++ b/server/channels/api4/shared_channel.go @@ -261,7 +261,7 @@ func getSharedChannelRemotes(c *Context, w http.ResponseWriter, r *http.Request) return } - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } diff --git a/server/channels/api4/shared_channel_metadata_test.go b/server/channels/api4/shared_channel_metadata_test.go index fd81ba1795..10da3499cb 100644 --- a/server/channels/api4/shared_channel_metadata_test.go +++ b/server/channels/api4/shared_channel_metadata_test.go @@ -149,7 +149,7 @@ func TestSharedChannelPostMetadataSync(t *testing.T) { }) // Create a local post with priority metadata - originalPost, appErr := th.App.CreatePost(th.Context, &model.Post{ + originalPost, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: testChannel.Id, Message: "Test post with priority metadata @" + th.BasicUser2.Username, @@ -204,7 +204,7 @@ func TestSharedChannelPostMetadataSync(t *testing.T) { }) // Create post with acknowledgement request - originalPost, appErr := th.App.CreatePost(th.Context, &model.Post{ + originalPost, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: testChannel.Id, Message: "Test post requesting acknowledgements @" + th.BasicUser2.Username, @@ -274,7 +274,7 @@ func TestSharedChannelPostMetadataSync(t *testing.T) { }) // Create post with acknowledgement request - originalPost, appErr := th.App.CreatePost(th.Context, &model.Post{ + originalPost, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: testChannel.Id, Message: "Test post for ack count sync @" + th.BasicUser2.Username, @@ -370,7 +370,7 @@ func TestSharedChannelPostMetadataSync(t *testing.T) { }) // Create post with persistent notifications enabled - _, appErr := th.App.CreatePost(th.Context, &model.Post{ + _, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: testChannel.Id, Message: "Test post with persistent notifications @" + th.BasicUser2.Username, @@ -535,7 +535,7 @@ func TestSharedChannelPostMetadataSync(t *testing.T) { // STEP 1: Server A creates a post with acknowledgement request t.Log("=== STEP 1: Server A creates post with ack request ===") - originalPost, appErr := th.App.CreatePost(th.Context, &model.Post{ + originalPost, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: testChannel.Id, Message: "Cross-cluster ack test - please acknowledge", diff --git a/server/channels/api4/system.go b/server/channels/api4/system.go index 67efc595a2..feeb93f19e 100644 --- a/server/channels/api4/system.go +++ b/server/channels/api4/system.go @@ -739,7 +739,9 @@ func pushNotificationAck(c *Context, w http.ResponseWriter, r *http.Request) { } // Return post data only when PostId is passed. if ack.PostId != "" && ack.NotificationType == model.PushTypeMessage { - if _, appErr := c.App.GetPostIfAuthorized(c.AppContext, ack.PostId, c.AppContext.Session(), false); appErr != nil { + var isMember bool + var appErr *model.AppError + if _, appErr, isMember = c.App.GetPostIfAuthorized(c.AppContext, ack.PostId, c.AppContext.Session(), false); appErr != nil { c.Err = appErr return } @@ -759,6 +761,14 @@ func pushNotificationAck(c *Context, w http.ResponseWriter, r *http.Request) { if err2 := json.NewEncoder(w).Encode(msg); err2 != nil { c.Logger.Warn("Error while writing response", mlog.Err(err2)) } + + auditRec := c.MakeAuditRecord("notificationAck", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "post_id", ack.PostId) + + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } } return diff --git a/server/channels/api4/upload.go b/server/channels/api4/upload.go index 7aa16469e0..05b50260a3 100644 --- a/server/channels/api4/upload.go +++ b/server/channels/api4/upload.go @@ -56,7 +56,7 @@ func createUpload(c *Context, w http.ResponseWriter, r *http.Request) { return } } else { - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), us.ChannelId, model.PermissionUploadFile) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), us.ChannelId, model.PermissionUploadFile); !ok { c.SetPermissionError(model.PermissionUploadFile) return } @@ -142,7 +142,10 @@ func uploadData(c *Context, w http.ResponseWriter, r *http.Request) { return } } else { - if us.UserId != c.AppContext.Session().UserId || !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), us.ChannelId, model.PermissionUploadFile) { + if us.UserId != c.AppContext.Session().UserId { + c.SetPermissionError(model.PermissionUploadFile) + return + } else if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), us.ChannelId, model.PermissionUploadFile); !ok { c.SetPermissionError(model.PermissionUploadFile) return } diff --git a/server/channels/api4/user.go b/server/channels/api4/user.go index f3b3a08bcb..c14198e911 100644 --- a/server/channels/api4/user.go +++ b/server/channels/api4/user.go @@ -933,7 +933,7 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) { profiles, appErr = c.App.GetUsersWithoutTeamPage(userGetOptions, c.IsSystemAdmin()) } else if notInChannelId != "" { - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), notInChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), notInChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -975,7 +975,7 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) { profiles, appErr = c.App.GetUsersInTeamPage(userGetOptions, c.IsSystemAdmin()) } } else if inChannelId != "" { - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), inChannelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), inChannelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -1182,14 +1182,18 @@ func searchUsers(c *Context, w http.ResponseWriter, r *http.Request) { } } - if props.InChannelId != "" && !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), props.InChannelId, model.PermissionReadChannel) { - c.SetPermissionError(model.PermissionReadChannel) - return + if props.InChannelId != "" { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), props.InChannelId, model.PermissionReadChannel); !ok { + c.SetPermissionError(model.PermissionReadChannel) + return + } } - if props.NotInChannelId != "" && !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), props.NotInChannelId, model.PermissionReadChannel) { - c.SetPermissionError(model.PermissionReadChannel) - return + if props.NotInChannelId != "" { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), props.NotInChannelId, model.PermissionReadChannel); !ok { + c.SetPermissionError(model.PermissionReadChannel) + return + } } if props.TeamId != "" && !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), props.TeamId, model.PermissionViewTeam) { @@ -1275,7 +1279,7 @@ func autocompleteUsers(c *Context, w http.ResponseWriter, r *http.Request) { } if channelId != "" { - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelId, model.PermissionReadChannel) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelId, model.PermissionReadChannel); !ok { c.SetPermissionError(model.PermissionReadChannel) return } @@ -3084,7 +3088,7 @@ func publishUserTyping(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.HasPermissionToChannel(c.AppContext, c.Params.UserId, typingRequest.ChannelId, model.PermissionCreatePost) { + if ok, _ := c.App.HasPermissionToChannel(c.AppContext, c.Params.UserId, typingRequest.ChannelId, model.PermissionCreatePost); !ok { c.SetPermissionError(model.PermissionCreatePost) return } @@ -3411,7 +3415,8 @@ func getThreadForUser(c *Context, w http.ResponseWriter, r *http.Request) { c.SetPermissionError(model.PermissionEditOtherUsers) return } - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.ThreadId, model.PermissionReadChannelContent) { + ok, isMember := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), c.Params.ThreadId) + if !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -3433,6 +3438,14 @@ func getThreadForUser(c *Context, w http.ResponseWriter, r *http.Request) { if err := json.NewEncoder(w).Encode(thread); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } + + auditRec := c.MakeAuditRecord("getThreadForUser", model.AuditStatusSuccess) + defer c.LogAuditRec(auditRec) + model.AddEventParameterToAuditRec(auditRec, "thread_id", c.Params.ThreadId) + + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } } func getThreadsForUser(c *Context, w http.ResponseWriter, r *http.Request) { @@ -3528,11 +3541,16 @@ func updateReadStateThreadByUser(c *Context, w http.ResponseWriter, r *http.Requ c.SetPermissionError(model.PermissionEditOtherUsers) return } - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.ThreadId, model.PermissionReadChannelContent) { + ok, isMember := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), c.Params.ThreadId) + if !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + thread, err := c.App.UpdateThreadReadForUser(c.AppContext, c.AppContext.Session().Id, c.Params.UserId, c.Params.TeamId, c.Params.ThreadId, c.Params.Timestamp) if err != nil { c.Err = err @@ -3564,10 +3582,14 @@ func setUnreadThreadByPostId(c *Context, w http.ResponseWriter, r *http.Request) return } - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.ThreadId, model.PermissionReadChannelContent) { + ok, isMember := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), c.Params.ThreadId) + if !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } // We want to make sure the thread is followed when marking as unread // https://mattermost.atlassian.net/browse/MM-36430 @@ -3606,7 +3628,7 @@ func unfollowThreadByUser(c *Context, w http.ResponseWriter, r *http.Request) { c.SetPermissionError(model.PermissionEditOtherUsers) return } - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.ThreadId, model.PermissionReadChannelContent) { + if ok, _ := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), c.Params.ThreadId); !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } @@ -3639,7 +3661,7 @@ func followThreadByUser(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.ThreadId, model.PermissionReadChannelContent) { + if ok, _ := c.App.SessionHasPermissionToReadPost(c.AppContext, *c.AppContext.Session(), c.Params.ThreadId); !ok { c.SetPermissionError(model.PermissionReadChannelContent) return } diff --git a/server/channels/api4/user_test.go b/server/channels/api4/user_test.go index 956ca6c59a..bf4de2578f 100644 --- a/server/channels/api4/user_test.go +++ b/server/channels/api4/user_test.go @@ -7523,7 +7523,7 @@ func TestThreadSocketEvents(t *testing.T) { require.NoError(t, err) CheckCreatedStatus(t, resp) - replyPost, appErr := th.App.CreatePostAsUser(th.Context, &model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply @" + th.BasicUser.Username, UserId: th.BasicUser2.Id, RootId: rpost.Id}, th.Context.Session().Id, false) + replyPost, _, appErr := th.App.CreatePostAsUser(th.Context, &model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply @" + th.BasicUser.Username, UserId: th.BasicUser2.Id, RootId: rpost.Id}, th.Context.Session().Id, false) require.Nil(t, appErr) defer func() { err = th.App.Srv().Store().Post().PermanentDeleteByUser(th.Context, th.BasicUser.Id) @@ -7700,7 +7700,7 @@ func TestThreadSocketEvents(t *testing.T) { for _, tc := range testCases { // post a reply on the thread - _, appErr = th.App.CreatePostAsUser(th.Context, tc.post, th.Context.Session().Id, false) + _, _, appErr = th.App.CreatePostAsUser(th.Context, tc.post, th.Context.Session().Id, false) require.Nil(t, appErr) var caught bool @@ -7734,18 +7734,18 @@ func TestThreadSocketEvents(t *testing.T) { rpost2 := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser2.Id, Message: "root post"} var appErr *model.AppError - rpost2, appErr = th.App.CreatePostAsUser(th.Context, rpost2, th.Context.Session().Id, false) + rpost2, _, appErr = th.App.CreatePostAsUser(th.Context, rpost2, th.Context.Session().Id, false) require.Nil(t, appErr) reply1 := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser2.Id, Message: "reply 1", RootId: rpost2.Id} reply2 := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser2.Id, Message: "reply 2", RootId: rpost2.Id} reply3 := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser2.Id, Message: "mention @" + th.BasicUser.Username, RootId: rpost2.Id} - _, appErr = th.App.CreatePostAsUser(th.Context, reply1, th.Context.Session().Id, false) + _, _, appErr = th.App.CreatePostAsUser(th.Context, reply1, th.Context.Session().Id, false) require.Nil(t, appErr) - _, appErr = th.App.CreatePostAsUser(th.Context, reply2, th.Context.Session().Id, false) + _, _, appErr = th.App.CreatePostAsUser(th.Context, reply2, th.Context.Session().Id, false) require.Nil(t, appErr) - _, appErr = th.App.CreatePostAsUser(th.Context, reply3, th.Context.Session().Id, false) + _, _, appErr = th.App.CreatePostAsUser(th.Context, reply3, th.Context.Session().Id, false) require.Nil(t, appErr) count := 0 diff --git a/server/channels/api4/webhook.go b/server/channels/api4/webhook.go index 0e63a2acac..eb40262535 100644 --- a/server/channels/api4/webhook.go +++ b/server/channels/api4/webhook.go @@ -50,7 +50,7 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { + if ok, _ := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel); !ok { c.LogAudit("fail - bad channel permissions") c.SetPermissionError(model.PermissionReadChannelContent) return @@ -154,10 +154,12 @@ func updateIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { return } - if channel.Type != model.ChannelTypeOpen && !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) { - c.LogAudit("fail - bad channel permissions") - c.SetPermissionError(model.PermissionReadChannelContent) - return + if channel.Type != model.ChannelTypeOpen { + if ok, _ := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel); !ok { + c.LogAudit("fail - bad channel permissions") + c.SetPermissionError(model.PermissionReadChannelContent) + return + } } incomingHook, err := c.App.UpdateIncomingWebhook(oldHook, &updatedHook) @@ -275,8 +277,14 @@ func getIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageIncomingWebhooks) || - (channel.Type != model.ChannelTypeOpen && !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel)) { + isPrivate := channel.Type != model.ChannelTypeOpen + restrictedChannel := false + if isPrivate { + hasChannelPermission, _ := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + restrictedChannel = !hasChannelPermission + } + + if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageIncomingWebhooks) || restrictedChannel { c.LogAudit("fail - bad permissions") c.SetPermissionError(model.PermissionManageIncomingWebhooks) return @@ -329,8 +337,14 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { auditRec.AddMeta("channel_name", channel.Name) auditRec.AddMeta("team_id", hook.TeamId) - if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageIncomingWebhooks) || - (channel.Type != model.ChannelTypeOpen && !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel)) { + isPrivate := channel.Type != model.ChannelTypeOpen + restrictedChannel := false + if isPrivate { + hasChannelPermission, _ := c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel) + restrictedChannel = !hasChannelPermission + } + + if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageIncomingWebhooks) || restrictedChannel { c.LogAudit("fail - bad permissions") c.SetPermissionError(model.PermissionManageIncomingWebhooks) return @@ -481,13 +495,13 @@ func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) { ) if channelID != "" { - if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelID, model.PermissionManageOutgoingWebhooks) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelID, model.PermissionManageOutgoingWebhooks); !ok { c.SetPermissionError(model.PermissionManageOutgoingWebhooks) return } // Remove userId as a filter if they have permission to manage others. - if c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelID, model.PermissionManageOthersOutgoingWebhooks) { + if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelID, model.PermissionManageOthersOutgoingWebhooks); ok { userID = "" } diff --git a/server/channels/app/authorization.go b/server/channels/app/authorization.go index 70669fd2be..327bf6d332 100644 --- a/server/channels/app/authorization.go +++ b/server/channels/app/authorization.go @@ -91,42 +91,52 @@ func (a *App) SessionHasPermissionToTeams(c request.CTX, session model.Session, return true } -func (a *App) SessionHasPermissionToChannel(c request.CTX, session model.Session, channelID string, permission *model.Permission) bool { +// SessionHasPermissionToChannel checks if the session has permission to the given channel. +// +// Returns: +// +// (hasPermission, isMember) +// +// hasPermission: true if the user has the specified permission for the channel, otherwise false. +// isMember: used for auditing access without membership. True if the user is a member of the channel, otherwise false. +func (a *App) SessionHasPermissionToChannel(c request.CTX, session model.Session, channelID string, permission *model.Permission) (hasPermission bool, isMember bool) { if channelID == "" { - return false + return false, false } channel, appErr := a.GetChannel(c, channelID) if appErr != nil && appErr.StatusCode == http.StatusNotFound { - return false + return false, false } else if appErr != nil { c.Logger().Warn("Failed to get channel", mlog.String("channel_id", channelID), mlog.Err(appErr)) } - if session.IsUnrestricted() || a.RolesGrantPermission(session.GetUserRoles(), model.PermissionManageSystem.Id) { - return true + if session.IsUnrestricted() { + return true, false } if appErr == nil && a.isChannelArchivedAndHidden(channel) { - return false + return false, false } + isMember = false ids, err := a.Srv().Store().Channel().GetAllChannelMembersForUser(c, session.UserId, true, true) var channelRoles []string if err == nil { if roles, ok := ids[channelID]; ok { + isMember = true channelRoles = strings.Fields(roles) if a.RolesGrantPermission(channelRoles, permission.Id) { - return true + return true, isMember } } } if appErr == nil && channel.TeamId != "" { - return a.SessionHasPermissionToTeam(session, channel.TeamId, permission) + return a.SessionHasPermissionToTeam(session, channel.TeamId, permission), isMember } - return a.SessionHasPermissionTo(session, permission) + return a.SessionHasPermissionTo(session, permission), isMember } // SessionHasPermissionToChannels returns true only if user has access to all channels. @@ -221,6 +231,21 @@ func (a *App) SessionHasPermissionToChannelByPost(session model.Session, postID return a.SessionHasPermissionTo(session, permission) } +func (a *App) SessionHasPermissionToReadPost(rctx request.CTX, session model.Session, postID string) (hasPErmission bool, isMember bool) { + if postID == "" { + return false, false + } + + channel, err := a.Srv().Store().Channel().GetForPost(postID) + if err != nil { + // Original implementation (SessionHasPermissionToChannelByPost) still checks for + // general permissions even if the channel is not found, and some tests rely on this behavior. + return a.SessionHasPermissionTo(session, model.PermissionReadChannelContent), false + } + + return a.SessionHasPermissionToReadChannel(rctx, session, channel) +} + func (a *App) SessionHasPermissionToCategory(c request.CTX, session model.Session, userID, teamID, categoryId string) bool { if a.SessionHasPermissionTo(session, model.PermissionEditOtherUsers) { return true @@ -298,11 +323,21 @@ func (a *App) HasPermissionToTeam(c request.CTX, askingUserId string, teamID str return a.HasPermissionTo(askingUserId, permission) } -func (a *App) HasPermissionToChannel(c request.CTX, askingUserId string, channelID string, permission *model.Permission) bool { +// HasPermissionToChannel determines if the specified user has the given permission on the provided channel. +// +// Returns: +// +// (hasPermission, isMember) +// +// hasPermission: true if the user has the specified permission for the channel, otherwise false. +// isMember: used for auditing access without membership. True if the user is a member of the channel, otherwise false. +func (a *App) HasPermissionToChannel(c request.CTX, askingUserId string, channelID string, permission *model.Permission) (hasPermission bool, isMember bool) { if channelID == "" || askingUserId == "" { - return false + return false, false } + isMember = false + // We call GetAllChannelMembersForUser instead of just getting // a single member from the DB, because it's cache backed // and this is a very frequent call. @@ -310,19 +345,20 @@ func (a *App) HasPermissionToChannel(c request.CTX, askingUserId string, channel var channelRoles []string if err == nil { if roles, ok := ids[channelID]; ok { + isMember = true channelRoles = strings.Fields(roles) if a.RolesGrantPermission(channelRoles, permission.Id) { - return true + return true, isMember } } } channel, appErr := a.GetChannel(c, channelID) if appErr == nil && channel.TeamId != "" { - return a.HasPermissionToTeam(c, askingUserId, channel.TeamId, permission) + return a.HasPermissionToTeam(c, askingUserId, channel.TeamId, permission), isMember } - return a.HasPermissionTo(askingUserId, permission) + return a.HasPermissionTo(askingUserId, permission), isMember } func (a *App) HasPermissionToChannelByPost(c request.CTX, askingUserId string, postID string, permission *model.Permission) bool { @@ -411,34 +447,53 @@ func (a *App) SessionHasPermissionToManageBot(rctx request.CTX, session model.Se return nil } -func (a *App) SessionHasPermissionToReadChannel(c request.CTX, session model.Session, channel *model.Channel) bool { +// SessionHasPermissionToReadChannel checks whether the given session has permission +// to read the specified channel. +// +// Returns: +// +// (hasPermission, isMember) +// +// hasPermission: true if the user has permission to read the channel, false otherwise +// isMember: used for auditing access without membership. True if the user is a member of the channel, false otherwise +func (a *App) SessionHasPermissionToReadChannel(c request.CTX, session model.Session, channel *model.Channel) (hasPermission bool, isMember bool) { if session.IsUnrestricted() { - return true + return true, false } return a.HasPermissionToReadChannel(c, session.UserId, channel) } -func (a *App) HasPermissionToReadChannel(c request.CTX, userID string, channel *model.Channel) bool { +// HasPermissionToReadChannel determines if the specified user has permission to read the given channel. +// +// Returns: +// +// (hasPermission, isMember) +// +// hasPermission: true if the user has permission to read the channel, false otherwise +// isMember: used for auditing access without membership. True if the user is a member of the channel, false otherwise +func (a *App) HasPermissionToReadChannel(c request.CTX, userID string, channel *model.Channel) (hasPermission bool, isMember bool) { if a.isChannelArchivedAndHidden(channel) { - return false + return false, false } - if a.HasPermissionToChannel(c, userID, channel.Id, model.PermissionReadChannelContent) { - return true + + if ok, member := a.HasPermissionToChannel(c, userID, channel.Id, model.PermissionReadChannelContent); ok { + return true, member } if channel.Type == model.ChannelTypeOpen && !*a.Config().ComplianceSettings.Enable { - return a.HasPermissionToTeam(c, userID, channel.TeamId, model.PermissionReadPublicChannel) + return a.HasPermissionToTeam(c, userID, channel.TeamId, model.PermissionReadPublicChannel), false } - return false + return false, false } func (a *App) HasPermissionToChannelMemberCount(c request.CTX, userID string, channel *model.Channel) bool { if a.isChannelArchivedAndHidden(channel) { return false } - if a.HasPermissionToChannel(c, userID, channel.Id, model.PermissionReadChannelContent) { + + if ok, _ := a.HasPermissionToChannel(c, userID, channel.Id, model.PermissionReadChannelContent); ok { return true } diff --git a/server/channels/app/authorization_test.go b/server/channels/app/authorization_test.go index 86ddd218bf..8bf561ce50 100644 --- a/server/channels/app/authorization_test.go +++ b/server/channels/app/authorization_test.go @@ -238,7 +238,9 @@ func TestSessionHasPermissionToChannel(t *testing.T) { } t.Run("basic user can access basic channel", func(t *testing.T) { - assert.True(t, th.App.SessionHasPermissionToChannel(th.Context, session, th.BasicChannel.Id, model.PermissionAddReaction)) + ok, isMember := th.App.SessionHasPermissionToChannel(th.Context, session, th.BasicChannel.Id, model.PermissionAddReaction) + assert.True(t, ok) + assert.True(t, isMember) }) t.Run("basic user cannot access archived channel if setting is off", func(t *testing.T) { @@ -247,7 +249,9 @@ func TestSessionHasPermissionToChannel(t *testing.T) { }) err := th.App.DeleteChannel(th.Context, th.BasicChannel, th.SystemAdminUser.Id) require.Nil(t, err) - assert.False(t, th.App.SessionHasPermissionToChannel(th.Context, session, th.BasicChannel.Id, model.PermissionReadChannel)) + + ok, _ := th.App.SessionHasPermissionToChannel(th.Context, session, th.BasicChannel.Id, model.PermissionReadChannel) + assert.False(t, ok) }) t.Run("basic user can access archived channel if setting is on", func(t *testing.T) { @@ -256,7 +260,20 @@ func TestSessionHasPermissionToChannel(t *testing.T) { }) err := th.App.DeleteChannel(th.Context, th.BasicChannel, th.SystemAdminUser.Id) require.Nil(t, err) - assert.True(t, th.App.SessionHasPermissionToChannel(th.Context, session, th.BasicChannel.Id, model.PermissionReadChannel)) + ok, isMember := th.App.SessionHasPermissionToChannel(th.Context, session, th.BasicChannel.Id, model.PermissionReadChannel) + assert.True(t, ok) + assert.True(t, isMember) + }) + + t.Run("admin user can access channel if not a member", func(t *testing.T) { + adminSession := model.Session{ + UserId: th.SystemAdminUser.Id, + Roles: model.SystemAdminRoleId, + } + + ok, isMember := th.App.SessionHasPermissionToChannel(th.Context, adminSession, th.BasicChannel.Id, model.PermissionAddReaction) + assert.True(t, ok) + assert.False(t, isMember) }) t.Run("does not panic if fetching channel causes an error", func(t *testing.T) { @@ -287,7 +304,9 @@ func TestSessionHasPermissionToChannel(t *testing.T) { // If there's an error returned from the GetChannel call the code should continue to cascade and since there // are no session level permissions in this test case, the permission should be denied. - assert.False(t, th.App.SessionHasPermissionToChannel(th.Context, session, th.BasicUser.Id, model.PermissionAddReaction)) + ok, isMember := th.App.SessionHasPermissionToChannel(th.Context, session, th.BasicUser.Id, model.PermissionAddReaction) + assert.False(t, ok) + assert.False(t, isMember) // MM-63624, check with TeamSettings.ExperimentalViewArchivedChannels off th.App.Srv().SetStore(mainHelper.GetStore()) @@ -296,7 +315,9 @@ func TestSessionHasPermissionToChannel(t *testing.T) { }) th.App.Srv().SetStore(&mockStore) - assert.False(t, th.App.SessionHasPermissionToChannel(th.Context, session, th.BasicUser.Id, model.PermissionAddReaction)) + ok, isMember = th.App.SessionHasPermissionToChannel(th.Context, session, th.BasicUser.Id, model.PermissionAddReaction) + assert.False(t, ok) + assert.False(t, isMember) }) } @@ -796,6 +817,7 @@ func TestHasPermissionToReadChannel(t *testing.T) { channelIsOpen bool canReadPublicChannel bool expected bool + isAdmin bool }{ { name: "Cannot read archived channels if the config doesn't allow it", @@ -857,10 +879,25 @@ func TestHasPermissionToReadChannel(t *testing.T) { canReadPublicChannel: true, expected: true, }, + { + name: "Can read private channels if it is a sysadmin and it is not member of the channel", + configComplianceEnabled: false, + channelDeleted: false, + canReadChannel: false, + channelIsOpen: false, + canReadPublicChannel: false, + expected: true, + isAdmin: true, + }, } for _, tc := range ttcc { t.Run(tc.name, func(t *testing.T) { + user := th.BasicUser2 + if tc.isAdmin { + user = th.SystemAdminUser + } + th.App.UpdateConfig(func(cfg *model.Config) { configViewArchived := tc.configViewArchived configComplianceEnabled := tc.configComplianceEnabled @@ -880,7 +917,7 @@ func TestHasPermissionToReadChannel(t *testing.T) { channel = th.CreatePrivateChannel(th.Context, team) } if tc.canReadChannel { - _, err := th.App.AddUserToChannel(th.Context, th.BasicUser2, channel, false) + _, err := th.App.AddUserToChannel(th.Context, user, channel, false) require.Nil(t, err) } @@ -891,8 +928,13 @@ func TestHasPermissionToReadChannel(t *testing.T) { require.Nil(t, err) } - result := th.App.HasPermissionToReadChannel(th.Context, th.BasicUser2.Id, channel) + result, isMember := th.App.HasPermissionToReadChannel(th.Context, user.Id, channel) require.Equal(t, tc.expected, result) + if result { + require.Equal(t, tc.canReadChannel, isMember) + } else { + require.Equal(t, false, isMember) + } }) } } @@ -1006,3 +1048,226 @@ func TestHasPermissionToChannelByPost(t *testing.T) { require.Equal(t, true, th.App.HasPermissionToChannelByPost(th.Context, th.SystemAdminUser.Id, post.Id, model.PermissionReadChannel)) }) } + +func TestHasPermissionToChannel(t *testing.T) { + mainHelper.Parallel(t) + th := Setup(t).InitBasic() + + channel := th.CreateChannel(th.Context, th.BasicTeam) + _, appErr := th.App.AddUserToChannel(th.Context, th.BasicUser, channel, false) + assert.Nil(t, appErr) + + archivedChannel := th.CreateChannel(th.Context, th.BasicTeam) + _, appErr = th.App.AddUserToChannel(th.Context, th.BasicUser, archivedChannel, false) + assert.Nil(t, appErr) + appErr = th.App.DeleteChannel(th.Context, archivedChannel, th.SystemAdminUser.Id) + assert.Nil(t, appErr) + + t.Run("read channel", func(t *testing.T) { + ok, isMember := th.App.HasPermissionToChannel(th.Context, th.BasicUser.Id, channel.Id, model.PermissionReadChannel) + assert.True(t, ok) + assert.True(t, isMember) + + ok, isMember = th.App.HasPermissionToChannel(th.Context, th.BasicUser2.Id, channel.Id, model.PermissionReadChannel) + assert.False(t, ok) + assert.False(t, isMember) + }) + + t.Run("read archived channel", func(t *testing.T) { + ok, isMember := th.App.HasPermissionToChannel(th.Context, th.BasicUser.Id, archivedChannel.Id, model.PermissionReadChannel) + assert.True(t, ok) + assert.True(t, isMember) + + ok, isMember = th.App.HasPermissionToChannel(th.Context, th.BasicUser2.Id, archivedChannel.Id, model.PermissionReadChannel) + assert.False(t, ok) + assert.False(t, isMember) + }) + + t.Run("read public channel", func(t *testing.T) { + ok, isMember := th.App.HasPermissionToChannel(th.Context, th.BasicUser.Id, channel.Id, model.PermissionReadPublicChannel) + assert.True(t, ok) + assert.True(t, isMember) + + ok, isMember = th.App.HasPermissionToChannel(th.Context, th.BasicUser2.Id, channel.Id, model.PermissionReadPublicChannel) + assert.True(t, ok) + assert.False(t, isMember) + }) + + t.Run("read channel - user is admin", func(t *testing.T) { + ok, isMember := th.App.HasPermissionToChannel(th.Context, th.SystemAdminUser.Id, channel.Id, model.PermissionReadChannel) + assert.True(t, ok) + assert.False(t, isMember) + }) +} + +func TestSessionHasPermissionToReadChannel(t *testing.T) { + mainHelper.Parallel(t) + th := Setup(t).InitBasic() + + channel := th.CreateChannel(th.Context, th.BasicTeam) + _, appErr := th.App.AddUserToChannel(th.Context, th.BasicUser, channel, false) + assert.Nil(t, appErr) + + archivedChannel := th.CreateChannel(th.Context, th.BasicTeam) + _, appErr = th.App.AddUserToChannel(th.Context, th.BasicUser, archivedChannel, false) + assert.Nil(t, appErr) + appErr = th.App.DeleteChannel(th.Context, archivedChannel, th.SystemAdminUser.Id) + assert.Nil(t, appErr) + + t.Run("basic user can read channel", func(t *testing.T) { + session := model.Session{ + UserId: th.BasicUser.Id, + } + ok, isMember := th.App.SessionHasPermissionToReadChannel(th.Context, session, channel) + assert.True(t, ok) + assert.True(t, isMember) + }) + + t.Run("basic user cannot read channel if not a member and not public", func(t *testing.T) { + privateChannel := th.CreatePrivateChannel(th.Context, th.BasicTeam) + session := model.Session{ + UserId: th.BasicUser2.Id, + } + ok, isMember := th.App.SessionHasPermissionToReadChannel(th.Context, session, privateChannel) + assert.False(t, ok) + assert.False(t, isMember) + }) + + t.Run("basic user can read archived channel if member", func(t *testing.T) { + session := model.Session{ + UserId: th.BasicUser.Id, + } + ok, isMember := th.App.SessionHasPermissionToReadChannel(th.Context, session, archivedChannel) + assert.True(t, ok) + assert.True(t, isMember) + }) + + t.Run("non-member can read public channel", func(t *testing.T) { + session := model.Session{ + UserId: th.BasicUser2.Id, + } + ok, isMember := th.App.SessionHasPermissionToReadChannel(th.Context, session, channel) + assert.True(t, ok) + assert.False(t, isMember) + }) + + t.Run("admin can read any channel", func(t *testing.T) { + session := model.Session{ + UserId: th.SystemAdminUser.Id, + Roles: model.SystemAdminRoleId, + } + ok, isMember := th.App.SessionHasPermissionToReadChannel(th.Context, session, channel) + assert.True(t, ok) + assert.False(t, isMember) + }) +} + +func TestSessionHasPermissionToReadPost(t *testing.T) { + mainHelper.Parallel(t) + th := Setup(t).InitBasic() + + // Create a post in a public channel, ensure basic user can read it. + post, _, err := th.App.CreatePost(th.Context, &model.Post{ + UserId: th.BasicUser.Id, + ChannelId: th.BasicChannel.Id, + Message: "hello world", + }, th.BasicChannel, model.CreatePostFlags{}) + require.Nil(t, err) + + t.Run("basic user can read their post", func(t *testing.T) { + session := model.Session{ + UserId: th.BasicUser.Id, + } + ok, isMember := th.App.SessionHasPermissionToReadPost(th.Context, session, post.Id) + assert.True(t, ok) + assert.True(t, isMember) + }) + + t.Run("other member in channel can read post", func(t *testing.T) { + // Add BasicUser2 to channel + _, aerr := th.App.AddUserToChannel(th.Context, th.BasicUser2, th.BasicChannel, false) + require.Nil(t, aerr) + session := model.Session{ + UserId: th.BasicUser2.Id, + } + ok, isMember := th.App.SessionHasPermissionToReadPost(th.Context, session, post.Id) + assert.True(t, ok) + assert.True(t, isMember) + }) + + t.Run("non-member can read post in public channel", func(t *testing.T) { + // Remove BasicUser2 from channel + aerr := th.App.removeUserFromChannel(th.Context, th.BasicUser2.Id, th.SystemAdminUser.Id, th.BasicChannel) + assert.Nil(t, aerr) + session := model.Session{ + UserId: th.BasicUser2.Id, + } + ok, isMember := th.App.SessionHasPermissionToReadPost(th.Context, session, post.Id) + assert.True(t, ok) + assert.False(t, isMember) + }) + + t.Run("non-member cannot read post in private channel", func(t *testing.T) { + privateChan := th.CreatePrivateChannel(th.Context, th.BasicTeam) + privatePost, _, err := th.App.CreatePost(th.Context, &model.Post{ + UserId: th.BasicUser.Id, + ChannelId: privateChan.Id, + Message: "private message", + }, privateChan, model.CreatePostFlags{}) + require.Nil(t, err) + + session := model.Session{ + UserId: th.BasicUser2.Id, + } + ok, isMember := th.App.SessionHasPermissionToReadPost(th.Context, session, privatePost.Id) + assert.False(t, ok) + assert.False(t, isMember) + }) + + t.Run("admin can read post even if not a channel member", func(t *testing.T) { + privateChan := th.CreatePrivateChannel(th.Context, th.BasicTeam) + privatePost, _, err := th.App.CreatePost(th.Context, &model.Post{ + UserId: th.BasicUser.Id, + ChannelId: privateChan.Id, + Message: "private admin", + }, privateChan, model.CreatePostFlags{}) + require.Nil(t, err) + + session := model.Session{ + UserId: th.SystemAdminUser.Id, + Roles: model.SystemAdminRoleId, + } + ok, isMember := th.App.SessionHasPermissionToReadPost(th.Context, session, privatePost.Id) + assert.True(t, ok) + assert.False(t, isMember) + }) + + t.Run("returns false for empty postID", func(t *testing.T) { + session := model.Session{ + UserId: th.BasicUser.Id, + } + ok, isMember := th.App.SessionHasPermissionToReadPost(th.Context, session, "") + assert.False(t, ok) + assert.False(t, isMember) + }) + + t.Run("returns permission based on system level if postID is missing", func(t *testing.T) { + // To simulate a missing post, use a postID that doesn't exist + session := model.Session{ + UserId: th.SystemAdminUser.Id, + Roles: model.SystemAdminRoleId, + } + + ok, isMember := th.App.SessionHasPermissionToReadPost(th.Context, session, model.NewId()) + assert.True(t, ok) + assert.False(t, isMember) + + // Basic user, should be false + session = model.Session{ + UserId: th.BasicUser2.Id, + } + ok, isMember = th.App.SessionHasPermissionToReadPost(th.Context, session, model.NewId()) + assert.False(t, ok) + assert.False(t, isMember) + }) +} diff --git a/server/channels/app/auto_responder.go b/server/channels/app/auto_responder.go index 8c6c42a0dd..fd7be6fd29 100644 --- a/server/channels/app/auto_responder.go +++ b/server/channels/app/auto_responder.go @@ -77,7 +77,7 @@ func (a *App) SendAutoResponse(rctx request.CTX, channel *model.Channel, receive UserId: receiver.Id, } - if _, err := a.CreatePost(rctx, autoResponderPost, channel, model.CreatePostFlags{}); err != nil { + if _, _, err := a.CreatePost(rctx, autoResponderPost, channel, model.CreatePostFlags{}); err != nil { return false, err } diff --git a/server/channels/app/auto_responder_test.go b/server/channels/app/auto_responder_test.go index 5834987e8e..3570d31533 100644 --- a/server/channels/app/auto_responder_test.go +++ b/server/channels/app/auto_responder_test.go @@ -110,7 +110,7 @@ func TestSendAutoResponseIfNecessary(t *testing.T) { channel := th.CreateDmChannel(receiver) - savedPost, _ := th.App.CreatePost(th.Context, &model.Post{ + savedPost, _, _ := th.App.CreatePost(th.Context, &model.Post{ ChannelId: channel.Id, Message: NewTestId(), UserId: th.BasicUser.Id, @@ -141,7 +141,7 @@ func TestSendAutoResponseIfNecessary(t *testing.T) { channel := th.CreateDmChannel(receiver) - savedPost, _ := th.App.CreatePost(th.Context, &model.Post{ + savedPost, _, _ := th.App.CreatePost(th.Context, &model.Post{ ChannelId: channel.Id, Message: NewTestId(), UserId: th.BasicUser.Id, @@ -159,7 +159,7 @@ func TestSendAutoResponseIfNecessary(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - savedPost, _ := th.App.CreatePost(th.Context, &model.Post{ + savedPost, _, _ := th.App.CreatePost(th.Context, &model.Post{ ChannelId: th.BasicChannel.Id, Message: NewTestId(), UserId: th.BasicUser.Id, @@ -200,7 +200,7 @@ func TestSendAutoResponseIfNecessary(t *testing.T) { botUser, err := th.App.GetUser(bot.UserId) assert.Nil(t, err) - savedPost, _ := th.App.CreatePost(th.Context, &model.Post{ + savedPost, _, _ := th.App.CreatePost(th.Context, &model.Post{ ChannelId: channel.Id, Message: NewTestId(), UserId: botUser.Id, @@ -236,7 +236,7 @@ func TestSendAutoResponseIfNecessary(t *testing.T) { // which needs to be cleaned up. require.NoError(t, th.GetSqlStore().Post().PermanentDeleteByUser(th.Context, th.BasicUser.Id)) - savedPost, err := th.App.CreatePost(th.Context, &model.Post{ + savedPost, _, err := th.App.CreatePost(th.Context, &model.Post{ ChannelId: channel.Id, Message: patch.NotifyProps["auto_responder_message"], UserId: receiver.Id, @@ -274,7 +274,7 @@ func TestSendAutoResponseSuccess(t *testing.T) { userUpdated1, err := th.App.PatchUser(th.Context, user.Id, patch, true) require.Nil(t, err) - savedPost, _ := th.App.CreatePost(th.Context, &model.Post{ + savedPost, _, _ := th.App.CreatePost(th.Context, &model.Post{ ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", UserId: th.BasicUser.Id, @@ -319,7 +319,7 @@ func TestSendAutoResponseSuccessOnThread(t *testing.T) { userUpdated1, err := th.App.PatchUser(th.Context, user.Id, patch, true) require.Nil(t, err) - parentPost, _ := th.App.CreatePost(th.Context, &model.Post{ + parentPost, _, _ := th.App.CreatePost(th.Context, &model.Post{ ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", UserId: th.BasicUser.Id, @@ -327,7 +327,7 @@ func TestSendAutoResponseSuccessOnThread(t *testing.T) { th.BasicChannel, model.CreatePostFlags{SetOnline: true}) - savedPost, _ := th.App.CreatePost(th.Context, &model.Post{ + savedPost, _, _ := th.App.CreatePost(th.Context, &model.Post{ ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", UserId: th.BasicUser.Id, @@ -373,7 +373,7 @@ func TestSendAutoResponseFailure(t *testing.T) { userUpdated1, err := th.App.PatchUser(th.Context, user.Id, patch, true) require.Nil(t, err) - savedPost, _ := th.App.CreatePost(th.Context, &model.Post{ + savedPost, _, _ := th.App.CreatePost(th.Context, &model.Post{ ChannelId: th.BasicChannel.Id, Message: "zz" + model.NewId() + "a", UserId: th.BasicUser.Id, diff --git a/server/channels/app/bot.go b/server/channels/app/bot.go index 708d2e4453..1f95f04b7e 100644 --- a/server/channels/app/bot.go +++ b/server/channels/app/bot.go @@ -157,7 +157,7 @@ func (a *App) CreateBot(rctx request.CTX, bot *model.Bot) (*model.Bot, *model.Ap Message: T("api.bot.teams_channels.add_message_mobile"), } - if _, err := a.CreatePostAsUser(rctx, botAddPost, rctx.Session().Id, true); err != nil { + if _, _, err := a.CreatePostAsUser(rctx, botAddPost, rctx.Session().Id, true); err != nil { return nil, err } } @@ -565,7 +565,7 @@ func (a *App) notifySysadminsBotOwnerDeactivated(rctx request.CTX, userID string Type: model.PostTypeSystemGeneric, } - _, appErr = a.CreatePost(rctx, post, channel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = a.CreatePost(rctx, post, channel, model.CreatePostFlags{SetOnline: true}) if appErr != nil { return appErr } diff --git a/server/channels/app/channel.go b/server/channels/app/channel.go index c1a555f2bc..3e4a673aac 100644 --- a/server/channels/app/channel.go +++ b/server/channels/app/channel.go @@ -847,7 +847,7 @@ func (a *App) postChannelPrivacyMessage(c request.CTX, user *model.User, channel }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("postChannelPrivacyMessage", "api.channel.post_channel_privacy_message.error", nil, "", http.StatusInternalServerError).Wrap(err) } @@ -902,7 +902,7 @@ func (a *App) RestoreChannel(c request.CTX, channel *model.Channel, userID strin }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { c.Logger().Warn("Failed to post unarchive message", mlog.Err(err)) } } else { @@ -923,7 +923,7 @@ func (a *App) RestoreChannel(c request.CTX, channel *model.Channel, userID strin }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { c.Logger().Error("Failed to post unarchive message", mlog.Err(err)) } }) @@ -1560,7 +1560,7 @@ func (a *App) DeleteChannel(c request.CTX, channel *model.Channel, userID string }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { c.Logger().Warn("Failed to post archive message", mlog.Err(err)) } } else { @@ -1578,7 +1578,7 @@ func (a *App) DeleteChannel(c request.CTX, channel *model.Channel, userID string }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { c.Logger().Warn("Failed to post archive message", mlog.Err(err)) } } @@ -1900,7 +1900,7 @@ func (a *App) PostUpdateChannelHeaderMessage(c request.CTX, userID string, chann }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("", "api.channel.post_update_channel_header_message_and_forget.post.error", nil, "", http.StatusInternalServerError).Wrap(err) } @@ -1933,7 +1933,7 @@ func (a *App) PostUpdateChannelPurposeMessage(c request.CTX, userID string, chan "new_purpose": newChannelPurpose, }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("", "app.channel.post_update_channel_purpose_message.post.error", nil, "", http.StatusInternalServerError).Wrap(err) } @@ -1960,7 +1960,7 @@ func (a *App) PostUpdateChannelDisplayNameMessage(c request.CTX, userID string, }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("PostUpdateChannelDisplayNameMessage", "api.channel.post_update_channel_displayname_message_and_forget.create_post.error", nil, "", http.StatusInternalServerError).Wrap(err) } @@ -2477,7 +2477,7 @@ func (a *App) postJoinChannelMessage(c request.CTX, user *model.User, channel *m }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("postJoinChannelMessage", "api.channel.post_user_add_remove_message_and_forget.error", nil, "", http.StatusInternalServerError).Wrap(err) } @@ -2495,7 +2495,7 @@ func (a *App) postJoinTeamMessage(c request.CTX, user *model.User, channel *mode }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("postJoinTeamMessage", "api.channel.post_user_add_remove_message_and_forget.error", nil, "", http.StatusInternalServerError).Wrap(err) } @@ -2578,7 +2578,7 @@ func (a *App) postLeaveChannelMessage(c request.CTX, user *model.User, channel * }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("postLeaveChannelMessage", "api.channel.post_user_add_remove_message_and_forget.error", nil, "", http.StatusInternalServerError).Wrap(err) } @@ -2607,7 +2607,7 @@ func (a *App) PostAddToChannelMessage(c request.CTX, user *model.User, addedUser }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("postAddToChannelMessage", "api.channel.post_user_add_remove_message_and_forget.error", nil, "", http.StatusInternalServerError).Wrap(err) } @@ -2629,7 +2629,7 @@ func (a *App) postAddToTeamMessage(c request.CTX, user *model.User, addedUser *m }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("postAddToTeamMessage", "api.channel.post_user_add_remove_message_and_forget.error", nil, "", http.StatusInternalServerError).Wrap(err) } @@ -2661,7 +2661,7 @@ func (a *App) postRemoveFromChannelMessage(c request.CTX, removerUserId string, }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("postRemoveFromChannelMessage", "api.channel.post_user_add_remove_message_and_forget.error", nil, "", http.StatusInternalServerError).Wrap(err) } @@ -2894,9 +2894,16 @@ func (a *App) ValidateUserPermissionsOnChannels(c request.CTX, userId string, ch continue } - if channel.Type == model.ChannelTypePrivate && a.HasPermissionToChannel(c, userId, channelId, model.PermissionManagePrivateChannelMembers) { - allowedChannelIds = append(allowedChannelIds, channelId) - } else if channel.Type == model.ChannelTypeOpen && a.HasPermissionToChannel(c, userId, channelId, model.PermissionManagePublicChannelMembers) { + allowedPrivate := false + if channel.Type == model.ChannelTypePrivate { + allowedPrivate, _ = a.HasPermissionToChannel(c, userId, channelId, model.PermissionManagePrivateChannelMembers) + } + allowedPublic := false + if channel.Type == model.ChannelTypeOpen { + allowedPublic, _ = a.HasPermissionToChannel(c, userId, channelId, model.PermissionManagePublicChannelMembers) + } + + if allowedPrivate || allowedPublic { allowedChannelIds = append(allowedChannelIds, channelId) } else { c.Logger().Info("Invite users to team - no permission to add members to that channel. UserId: " + userId + " ChannelId: " + channelId) @@ -3453,7 +3460,7 @@ func (a *App) postChannelMoveMessage(c request.CTX, user *model.User, channel *m }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("postChannelMoveMessage", "api.team.move_channel.post.error", nil, "", http.StatusInternalServerError).Wrap(err) } @@ -3967,7 +3974,7 @@ func (a *App) postMessageForConvertGroupMessageToChannel(c request.CTX, channelI return appErr } - if _, appErr := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); appErr != nil { + if _, _, appErr := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); appErr != nil { c.Logger().Error("Failed to create post for notifying about GM converted to private channel", mlog.Err(appErr)) return model.NewAppError( diff --git a/server/channels/app/channel_test.go b/server/channels/app/channel_test.go index e3488c9760..2b1bab136a 100644 --- a/server/channels/app/channel_test.go +++ b/server/channels/app/channel_test.go @@ -269,7 +269,7 @@ func TestMoveChannel(t *testing.T) { ChannelId: channel.Id, Message: "test", } - post, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{}) + post, _, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{}) require.Nil(t, appErr) // Post a reply to the thread @@ -279,7 +279,7 @@ func TestMoveChannel(t *testing.T) { RootId: post.Id, Message: "reply", } - _, appErr = th.App.CreatePost(th.Context, reply, channel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, reply, channel, model.CreatePostFlags{}) require.Nil(t, appErr) // Check that the thread count before move @@ -772,7 +772,7 @@ func TestUsersAndPostsCreateActivityInChannel(t *testing.T) { Message: "root post", UserId: th.BasicUser.Id, } - _, err = th.App.CreatePost(th.Context, post, channel1, model.CreatePostFlags{}) + _, _, err = th.App.CreatePost(th.Context, post, channel1, model.CreatePostFlags{}) require.Nil(t, err, "Failed to create post.") _, err = th.App.AddUserToChannel(th.Context, user, channel2, false) @@ -799,7 +799,7 @@ func TestUsersAndPostsCreateActivityInChannel(t *testing.T) { } err = th.App.RemoveUserFromChannel(th.Context, user4.Id, user4.Id, channel4) require.Nil(t, err, "Failed to create post.") - _, err = th.App.CreatePost(th.Context, post2, channel5, model.CreatePostFlags{}) + _, _, err = th.App.CreatePost(th.Context, post2, channel5, model.CreatePostFlags{}) require.Nil(t, err, "Failed to create post.") _, err = th.App.AddUserToChannel(th.Context, user, channel6, false) require.Nil(t, err, "Failed to add user to channel.") @@ -845,7 +845,7 @@ func TestLeaveDefaultChannel(t *testing.T) { Message: "root post", UserId: th.BasicUser.Id, } - rpost, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) reply := &model.Post{ @@ -854,7 +854,7 @@ func TestLeaveDefaultChannel(t *testing.T) { UserId: th.BasicUser.Id, RootId: rpost.Id, } - _, appErr = th.App.CreatePost(th.Context, reply, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th.App.CreatePost(th.Context, reply, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) threads, appErr := th.App.GetThreadsForUser(th.BasicUser.Id, townSquare.TeamId, model.GetUserThreadsOpts{}) @@ -884,7 +884,7 @@ func TestLeaveChannel(t *testing.T) { UserId: th.BasicUser.Id, } - rpost, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) reply := &model.Post{ @@ -893,7 +893,7 @@ func TestLeaveChannel(t *testing.T) { UserId: th.BasicUser.Id, RootId: rpost.Id, } - _, appErr = th.App.CreatePost(th.Context, reply, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th.App.CreatePost(th.Context, reply, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) return rpost @@ -1805,7 +1805,7 @@ func TestMarkChannelAsUnreadFromPost(t *testing.T) { _, appErr := th.App.AddUserToChannel(th.Context, u2, c2, false) require.Nil(t, appErr) - p4, appErr := th.App.CreatePost(th.Context, &model.Post{ + p4, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: u2.Id, ChannelId: c2.Id, Message: "@" + u1.Username, @@ -1813,7 +1813,7 @@ func TestMarkChannelAsUnreadFromPost(t *testing.T) { require.Nil(t, appErr) th.CreatePost(c2) - _, appErr = th.App.CreatePost(th.Context, &model.Post{ + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{ UserId: u2.Id, ChannelId: c2.Id, RootId: p4.Id, @@ -1841,7 +1841,7 @@ func TestMarkChannelAsUnreadFromPost(t *testing.T) { th.CreatePost(dc) th.CreatePost(dc) - _, appErr := th.App.CreatePost(th.Context, &model.Post{ChannelId: dc.Id, UserId: th.BasicUser.Id, Message: "testReply", RootId: dm1.Id}, dc, model.CreatePostFlags{}) + _, _, appErr := th.App.CreatePost(th.Context, &model.Post{ChannelId: dc.Id, UserId: th.BasicUser.Id, Message: "testReply", RootId: dm1.Id}, dc, model.CreatePostFlags{}) assert.Nil(t, appErr) response, appErr := th.App.MarkChannelAsUnreadFromPost(th.Context, dm1.Id, u2.Id, true) @@ -2491,11 +2491,13 @@ func TestPatchChannelModerationsForChannel(t *testing.T) { _, appErr := th.App.PatchChannelModerationsForChannel(th.Context, channel.DeepCopy(), addCreatePosts) require.Nil(t, appErr) - require.True(t, th.App.SessionHasPermissionToChannel(th.Context, mockSession, channel.Id, model.PermissionCreatePost)) + ok, _ := th.App.SessionHasPermissionToChannel(th.Context, mockSession, channel.Id, model.PermissionCreatePost) + require.True(t, ok) _, appErr = th.App.PatchChannelModerationsForChannel(th.Context, channel.DeepCopy(), removeCreatePosts) require.Nil(t, appErr) - require.False(t, th.App.SessionHasPermissionToChannel(th.Context, mockSession, channel.Id, model.PermissionCreatePost)) + ok, _ = th.App.SessionHasPermissionToChannel(th.Context, mockSession, channel.Id, model.PermissionCreatePost) + require.False(t, ok) }) } @@ -2609,7 +2611,7 @@ func TestViewChannelCollapsedThreadsTurnedOff(t *testing.T) { Message: "root post @" + u1.Username, UserId: u2.Id, } - rpost1, appErr := th.App.CreatePost(th.Context, post1, c1, model.CreatePostFlags{SetOnline: true}) + rpost1, _, appErr := th.App.CreatePost(th.Context, post1, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) // mention the user in a reply post @@ -2619,7 +2621,7 @@ func TestViewChannelCollapsedThreadsTurnedOff(t *testing.T) { UserId: u2.Id, RootId: rpost1.Id, } - _, appErr = th.App.CreatePost(th.Context, post2, c1, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th.App.CreatePost(th.Context, post2, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) // Check we have unread mention in the thread @@ -2686,19 +2688,19 @@ func TestMarkChannelAsUnreadFromPostCollapsedThreadsTurnedOff(t *testing.T) { // user1: a root post // user2: Another root mention @u1 user1Mention := " @" + th.BasicUser.Username - rootPost1, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "first root mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) + rootPost1, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "first root mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hello"}, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hello"}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - replyPost1, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) + replyPost1, _, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another reply"}, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another reply"}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "a root post"}, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "a root post"}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another root mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another root mention" + user1Mention}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) t.Run("Mark reply post as unread", func(t *testing.T) { @@ -2756,17 +2758,17 @@ func TestMarkUnreadCRTOffUpdatesThreads(t *testing.T) { appErr := th.App.PermanentDeleteUser(th.Context, user3) require.Nil(t, appErr) }() - rootPost, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "root post"}, th.BasicChannel, model.CreatePostFlags{}) + rootPost, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "root post"}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - r1, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "reply 1"}, th.BasicChannel, model.CreatePostFlags{}) + r1, _, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "reply 1"}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "reply 2 @" + user3.Username}, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "reply 2 @" + user3.Username}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "reply 3"}, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "reply 3"}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) editedPost := r1.Clone() editedPost.Message += " edited" - _, appErr = th.App.UpdatePost(th.Context, editedPost, &model.UpdatePostOptions{SafeUpdate: false}) + _, _, appErr = th.App.UpdatePost(th.Context, editedPost, &model.UpdatePostOptions{SafeUpdate: false}) require.Nil(t, appErr) th.LinkUserToTeam(user3, th.BasicTeam) @@ -3309,7 +3311,7 @@ func TestGetChannelFileCount(t *testing.T) { Message: "This is a test post", UserId: th.BasicUser.Id, } - post, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{}) + post, _, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{}) require.Nil(t, appErr) fileInfo1 := &model.FileInfo{ diff --git a/server/channels/app/command.go b/server/channels/app/command.go index d855005c44..675e996fc5 100644 --- a/server/channels/app/command.go +++ b/server/channels/app/command.go @@ -70,7 +70,12 @@ func (a *App) CreateCommandPost(c request.CTX, post *model.Post, teamID string, } if response.ResponseType == model.CommandResponseTypeInChannel { - return a.CreatePostMissingChannel(c, post, true, true) + // The post is only used for tests, so even if there are membership issues, we won't send the post to the client. + createdPost, _, appErr := a.CreatePostMissingChannel(c, post, true, true) + if appErr != nil { + return nil, appErr + } + return createdPost, nil } if (response.ResponseType == "" || response.ResponseType == model.CommandResponseTypeEphemeral) && (response.Text != "" || response.Attachments != nil) { diff --git a/server/channels/app/export_test.go b/server/channels/app/export_test.go index ccc71db7e5..d29aa29748 100644 --- a/server/channels/app/export_test.go +++ b/server/channels/app/export_test.go @@ -556,7 +556,7 @@ func TestExportDMandGMPost(t *testing.T) { Message: "aa" + model.NewId() + "a", UserId: th1.BasicUser.Id, } - _, appErr := th1.App.CreatePost(th1.Context, p1, dmChannel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr := th1.App.CreatePost(th1.Context, p1, dmChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) p2 := &model.Post{ @@ -564,7 +564,7 @@ func TestExportDMandGMPost(t *testing.T) { Message: "bb" + model.NewId() + "a", UserId: th1.BasicUser.Id, } - _, appErr = th1.App.CreatePost(th1.Context, p2, dmChannel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th1.App.CreatePost(th1.Context, p2, dmChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) // GM posts @@ -573,7 +573,7 @@ func TestExportDMandGMPost(t *testing.T) { Message: "cc" + model.NewId() + "a", UserId: th1.BasicUser.Id, } - _, appErr = th1.App.CreatePost(th1.Context, p3, gmChannel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th1.App.CreatePost(th1.Context, p3, gmChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) p4 := &model.Post{ @@ -581,7 +581,7 @@ func TestExportDMandGMPost(t *testing.T) { Message: "dd" + model.NewId() + "a", UserId: th1.BasicUser.Id, } - _, appErr = th1.App.CreatePost(th1.Context, p4, gmChannel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th1.App.CreatePost(th1.Context, p4, gmChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) posts, err := th1.App.Srv().Store().Post().GetDirectPostParentsForExportAfter(1000, "0000000", false) @@ -646,7 +646,7 @@ func TestExportPostWithProps(t *testing.T) { }, UserId: th1.BasicUser.Id, } - _, appErr := th1.App.CreatePost(th1.Context, p1, dmChannel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr := th1.App.CreatePost(th1.Context, p1, dmChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) p2 := &model.Post{ @@ -657,7 +657,7 @@ func TestExportPostWithProps(t *testing.T) { }, UserId: th1.BasicUser.Id, } - _, appErr = th1.App.CreatePost(th1.Context, p2, gmChannel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th1.App.CreatePost(th1.Context, p2, gmChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) posts, err := th1.App.Srv().Store().Post().GetDirectPostParentsForExportAfter(1000, "0000000", false) @@ -1079,7 +1079,7 @@ func TestBuildPostReplies(t *testing.T) { fileIDs = append(fileIDs, info.Id) } - post, err := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, RootId: rootID, FileIds: fileIDs}, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, err := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, RootId: rootID, FileIds: fileIDs}, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) return post @@ -1640,7 +1640,7 @@ func TestExportDeactivatedUserDMs(t *testing.T) { Message: initialMessage, UserId: th1.BasicUser.Id, } - initialPostCreated, appErr := th1.App.CreatePost(th1.Context, initialPost, dmChannel, model.CreatePostFlags{SetOnline: true}) + initialPostCreated, _, appErr := th1.App.CreatePost(th1.Context, initialPost, dmChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) // 2. Have user2 reply with TWO types of replies: @@ -1653,7 +1653,7 @@ func TestExportDeactivatedUserDMs(t *testing.T) { UserId: user2.Id, RootId: initialPostCreated.Id, // This makes it a threaded reply } - _, appErr = th1.App.CreatePost(th1.Context, threadedReply, dmChannel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th1.App.CreatePost(th1.Context, threadedReply, dmChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) // 2b. User2 sends a standalone reply (NOT in a thread) @@ -1664,7 +1664,7 @@ func TestExportDeactivatedUserDMs(t *testing.T) { UserId: user2.Id, // No RootId, making it a standalone message, not a thread reply } - _, appErr = th1.App.CreatePost(th1.Context, nonThreadedReply, dmChannel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th1.App.CreatePost(th1.Context, nonThreadedReply, dmChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) // 3. Now deactivate user2 diff --git a/server/channels/app/file.go b/server/channels/app/file.go index aa1a79a014..075dd543fc 100644 --- a/server/channels/app/file.go +++ b/server/channels/app/file.go @@ -1460,12 +1460,12 @@ func populateZipfile(w *zip.Writer, fileDatas []model.FileData) error { return nil } -func (a *App) SearchFilesInTeamForUser(c request.CTX, terms string, userId string, teamId string, isOrSearch bool, includeDeletedChannels bool, timeZoneOffset int, page, perPage int) (*model.FileInfoList, *model.AppError) { +func (a *App) SearchFilesInTeamForUser(c request.CTX, terms string, userId string, teamId string, isOrSearch bool, includeDeletedChannels bool, timeZoneOffset int, page, perPage int) (*model.FileInfoList, bool, *model.AppError) { paramsList := model.ParseSearchParams(strings.TrimSpace(terms), timeZoneOffset) includeDeleted := includeDeletedChannels && *a.Config().TeamSettings.ExperimentalViewArchivedChannels if !*a.Config().ServiceSettings.EnableFileSearch { - return nil, model.NewAppError("SearchFilesInTeamForUser", "store.sql_file_info.search.disabled", nil, fmt.Sprintf("teamId=%v userId=%v", teamId, userId), http.StatusNotImplemented) + return nil, false, model.NewAppError("SearchFilesInTeamForUser", "store.sql_file_info.search.disabled", nil, fmt.Sprintf("teamId=%v userId=%v", teamId, userId), http.StatusNotImplemented) } finalParamsList := []*model.SearchParams{} @@ -1489,7 +1489,7 @@ func (a *App) SearchFilesInTeamForUser(c request.CTX, terms string, userId strin // If the processed search params are empty, return empty search results. if len(finalParamsList) == 0 { - return model.NewFileInfoList(), nil + return model.NewFileInfoList(), true, nil } fileInfoSearchResults, nErr := a.Srv().Store().FileInfo().Search(c, finalParamsList, userId, teamId, page, perPage) @@ -1497,26 +1497,27 @@ func (a *App) SearchFilesInTeamForUser(c request.CTX, terms string, userId strin var appErr *model.AppError switch { case errors.As(nErr, &appErr): - return nil, appErr + return nil, false, appErr default: - return nil, model.NewAppError("SearchFilesInTeamForUser", "app.post.search.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) + return nil, false, model.NewAppError("SearchFilesInTeamForUser", "app.post.search.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) } } if appErr := a.filterInaccessibleFiles(fileInfoSearchResults, filterFileOptions{assumeSortedCreatedAt: true}); appErr != nil { - return nil, appErr + return nil, false, appErr } - if appErr := a.FilterFilesByChannelPermissions(c, fileInfoSearchResults, userId); appErr != nil { - return nil, appErr + allFilesHaveMembership, appErr := a.FilterFilesByChannelPermissions(c, fileInfoSearchResults, userId) + if appErr != nil { + return nil, false, appErr } - return fileInfoSearchResults, nil + return fileInfoSearchResults, allFilesHaveMembership, nil } -func (a *App) FilterFilesByChannelPermissions(rctx request.CTX, fileList *model.FileInfoList, userID string) *model.AppError { +func (a *App) FilterFilesByChannelPermissions(rctx request.CTX, fileList *model.FileInfoList, userID string) (bool, *model.AppError) { if fileList == nil || fileList.FileInfos == nil || len(fileList.FileInfos) == 0 { - return nil + return true, nil // On an empty file list, we consider all files as having membership } channels := make(map[string]*model.Channel) @@ -1530,7 +1531,7 @@ func (a *App) FilterFilesByChannelPermissions(rctx request.CTX, fileList *model. channelIDs := slices.Collect(maps.Keys(channels)) channelList, err := a.GetChannels(rctx, channelIDs) if err != nil && err.StatusCode != http.StatusNotFound { - return err + return false, err } for _, channel := range channelList { channels[channel.Id] = channel @@ -1540,6 +1541,7 @@ func (a *App) FilterFilesByChannelPermissions(rctx request.CTX, fileList *model. channelReadPermission := make(map[string]bool) filteredFiles := make(map[string]*model.FileInfo) filteredOrder := []string{} + allFilesHaveMembership := true for _, fileID := range fileList.Order { fileInfo, ok := fileList.FileInfos[fileID] @@ -1550,10 +1552,14 @@ func (a *App) FilterFilesByChannelPermissions(rctx request.CTX, fileList *model. if _, ok := channelReadPermission[fileInfo.ChannelId]; !ok { channel := channels[fileInfo.ChannelId] allowed := false + isMember := true if channel != nil { - allowed = a.HasPermissionToReadChannel(rctx, userID, channel) + allowed, isMember = a.HasPermissionToReadChannel(rctx, userID, channel) } channelReadPermission[fileInfo.ChannelId] = allowed + if allowed { + allFilesHaveMembership = allFilesHaveMembership && isMember + } } if channelReadPermission[fileInfo.ChannelId] { @@ -1565,7 +1571,7 @@ func (a *App) FilterFilesByChannelPermissions(rctx request.CTX, fileList *model. fileList.FileInfos = filteredFiles fileList.Order = filteredOrder - return nil + return allFilesHaveMembership, nil } func (a *App) ExtractContentFromFileInfo(rctx request.CTX, fileInfo *model.FileInfo) error { diff --git a/server/channels/app/file_test.go b/server/channels/app/file_test.go index cc30ae1ffa..80e015bcc6 100644 --- a/server/channels/app/file_test.go +++ b/server/channels/app/file_test.go @@ -299,13 +299,13 @@ func TestMigrateFilenamesToFileInfos(t *testing.T) { fpath := fmt.Sprintf("/teams/%v/channels/%v/users/%v/%v/test.png", th.BasicTeam.Id, th.BasicChannel.Id, th.BasicUser.Id, fileID) _, err := th.App.WriteFile(file, fpath) require.Nil(t, err) - rpost, err := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Filenames: []string{fmt.Sprintf("/%v/%v/%v/test.png", th.BasicChannel.Id, th.BasicUser.Id, fileID)}}, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, err := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Filenames: []string{fmt.Sprintf("/%v/%v/%v/test.png", th.BasicChannel.Id, th.BasicUser.Id, fileID)}}, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) infos = th.App.MigrateFilenamesToFileInfos(th.Context, rpost) assert.Equal(t, 1, len(infos)) - rpost, err = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Filenames: []string{fmt.Sprintf("/%v/%v/%v/../../test.png", th.BasicChannel.Id, th.BasicUser.Id, fileID)}}, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, err = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Filenames: []string{fmt.Sprintf("/%v/%v/%v/../../test.png", th.BasicChannel.Id, th.BasicUser.Id, fileID)}}, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) infos = th.App.MigrateFilenamesToFileInfos(th.Context, rpost) @@ -482,7 +482,7 @@ func TestSearchFilesInTeamForUser(t *testing.T) { page := 0 - results, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + results, allFilesHaveMembership, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) require.Nil(t, err) require.NotNil(t, results) @@ -495,6 +495,7 @@ func TestSearchFilesInTeamForUser(t *testing.T) { fileInfos[1].Id, fileInfos[0].Id, }, results.Order) + require.True(t, allFilesHaveMembership) }) t.Run("should not return later pages of fileInfos from database", func(t *testing.T) { @@ -503,11 +504,12 @@ func TestSearchFilesInTeamForUser(t *testing.T) { page := 1 - results, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + results, allFilesHaveMembership, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) require.Nil(t, err) require.NotNil(t, results) assert.Equal(t, []string{}, results.Order) + require.True(t, allFilesHaveMembership) }) t.Run("should return first page of fileInfos from ElasticSearch", func(t *testing.T) { @@ -533,11 +535,12 @@ func TestSearchFilesInTeamForUser(t *testing.T) { th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil }() - results, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + results, allFilesHaveMembership, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) require.Nil(t, err) require.NotNil(t, results) assert.Equal(t, resultsPage, results.Order) + require.True(t, allFilesHaveMembership) es.AssertExpectations(t) }) @@ -561,11 +564,12 @@ func TestSearchFilesInTeamForUser(t *testing.T) { th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil }() - results, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + results, allFilesHaveMembership, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) require.Nil(t, err) require.NotNil(t, results) assert.Equal(t, resultsPage, results.Order) + require.True(t, allFilesHaveMembership) es.AssertExpectations(t) }) @@ -586,7 +590,7 @@ func TestSearchFilesInTeamForUser(t *testing.T) { th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil }() - results, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + results, allFilesHaveMembership, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) require.Nil(t, err) require.NotNil(t, results) @@ -599,6 +603,7 @@ func TestSearchFilesInTeamForUser(t *testing.T) { fileInfos[1].Id, fileInfos[0].Id, }, results.Order) + require.True(t, allFilesHaveMembership) es.AssertExpectations(t) }) @@ -619,10 +624,11 @@ func TestSearchFilesInTeamForUser(t *testing.T) { th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil }() - results, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + results, allFilesHaveMembership, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) require.Nil(t, err) assert.Equal(t, []string{}, results.Order) + require.True(t, allFilesHaveMembership) es.AssertExpectations(t) }) } @@ -749,16 +755,18 @@ func TestSetFileSearchableContent(t *testing.T) { }) require.NoError(t, err) - result, appErr := th.App.SearchFilesInTeamForUser(th.Context, "searchable", th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, 0, 60) + result, allFilesHaveMembership, appErr := th.App.SearchFilesInTeamForUser(th.Context, "searchable", th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, 0, 60) require.Nil(t, appErr) assert.Equal(t, 0, len(result.Order)) + require.True(t, allFilesHaveMembership) appErr = th.App.SetFileSearchableContent(th.Context, fileInfo.Id, "searchable") require.Nil(t, appErr) - result, appErr = th.App.SearchFilesInTeamForUser(th.Context, "searchable", th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, 0, 60) + result, allFilesHaveMembership, appErr = th.App.SearchFilesInTeamForUser(th.Context, "searchable", th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, 0, 60) require.Nil(t, appErr) assert.Equal(t, 1, len(result.Order)) + require.True(t, allFilesHaveMembership) } func TestPermanentDeleteFilesByPost(t *testing.T) { @@ -786,7 +794,7 @@ func TestPermanentDeleteFilesByPost(t *testing.T) { FileIds: []string{info1.Id}, } - post, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) assert.Nil(t, err) err = th.App.PermanentDeleteFilesByPost(th.Context, post.Id) @@ -810,7 +818,7 @@ func TestPermanentDeleteFilesByPost(t *testing.T) { CreateAt: 0, } - post, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) assert.Nil(t, err) err = th.App.PermanentDeleteFilesByPost(th.Context, post.Id) @@ -853,7 +861,7 @@ func TestFilterFilesByChannelPermissions(t *testing.T) { fileList.Order = []string{fileInfo1.Id, fileInfo2.Id, fileInfo3.Id} // BasicUser should have access to all files - appErr := th.App.FilterFilesByChannelPermissions(th.Context, fileList, th.BasicUser.Id) + _, appErr := th.App.FilterFilesByChannelPermissions(th.Context, fileList, th.BasicUser.Id) require.Nil(t, appErr) require.Len(t, fileList.FileInfos, 3) require.Len(t, fileList.Order, 3) @@ -866,7 +874,7 @@ func TestFilterFilesByChannelPermissions(t *testing.T) { fileList.FileInfos[fileInfo3.Id] = fileInfo3 fileList.Order = []string{fileInfo1.Id, fileInfo2.Id, fileInfo3.Id} - appErr := th.App.FilterFilesByChannelPermissions(th.Context, fileList, guestUser.Id) + _, appErr := th.App.FilterFilesByChannelPermissions(th.Context, fileList, guestUser.Id) require.Nil(t, appErr) require.Len(t, fileList.FileInfos, 3) require.Len(t, fileList.Order, 3) @@ -904,7 +912,7 @@ func TestFilterFilesByChannelPermissions(t *testing.T) { fileList.FileInfos[fileInfo3.Id] = fileInfo3 fileList.Order = []string{fileInfo1.Id, fileInfo2.Id, fileInfo3.Id} - appErr = th.App.FilterFilesByChannelPermissions(th.Context, fileList, guestUser.Id) + _, appErr = th.App.FilterFilesByChannelPermissions(th.Context, fileList, guestUser.Id) require.Nil(t, appErr) require.Len(t, fileList.FileInfos, 0) require.Len(t, fileList.Order, 0) @@ -912,14 +920,14 @@ func TestFilterFilesByChannelPermissions(t *testing.T) { t.Run("should handle empty file list", func(t *testing.T) { fileList := model.NewFileInfoList() - appErr := th.App.FilterFilesByChannelPermissions(th.Context, fileList, th.BasicUser.Id) + _, appErr := th.App.FilterFilesByChannelPermissions(th.Context, fileList, th.BasicUser.Id) require.Nil(t, appErr) require.Len(t, fileList.FileInfos, 0) require.Len(t, fileList.Order, 0) }) t.Run("should handle nil file list", func(t *testing.T) { - appErr := th.App.FilterFilesByChannelPermissions(th.Context, nil, th.BasicUser.Id) + _, appErr := th.App.FilterFilesByChannelPermissions(th.Context, nil, th.BasicUser.Id) require.Nil(t, appErr) }) @@ -933,7 +941,7 @@ func TestFilterFilesByChannelPermissions(t *testing.T) { fileList.FileInfos[fileWithoutChannel.Id] = fileWithoutChannel fileList.Order = []string{fileWithoutChannel.Id} - appErr := th.App.FilterFilesByChannelPermissions(th.Context, fileList, th.BasicUser.Id) + _, appErr := th.App.FilterFilesByChannelPermissions(th.Context, fileList, th.BasicUser.Id) require.Nil(t, appErr) require.Len(t, fileList.FileInfos, 0) require.Len(t, fileList.Order, 0) @@ -949,7 +957,7 @@ func TestFilterFilesByChannelPermissions(t *testing.T) { fileList.FileInfos[fileWithInvalidChannel.Id] = fileWithInvalidChannel fileList.Order = []string{fileWithInvalidChannel.Id} - appErr := th.App.FilterFilesByChannelPermissions(th.Context, fileList, th.BasicUser.Id) + _, appErr := th.App.FilterFilesByChannelPermissions(th.Context, fileList, th.BasicUser.Id) require.Nil(t, appErr) require.Len(t, fileList.FileInfos, 0) require.Len(t, fileList.Order, 0) diff --git a/server/channels/app/helper_test.go b/server/channels/app/helper_test.go index 9f54dfc143..2599a298ef 100644 --- a/server/channels/app/helper_test.go +++ b/server/channels/app/helper_test.go @@ -518,7 +518,7 @@ func (th *TestHelper) CreatePost(channel *model.Channel, postOptions ...PostOpti } var err *model.AppError - if post, err = th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if post, _, err = th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { panic(err) } return post @@ -533,7 +533,7 @@ func (th *TestHelper) CreateMessagePost(channel *model.Channel, message string) } var err *model.AppError - if post, err = th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if post, _, err = th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { panic(err) } return post @@ -553,7 +553,7 @@ func (th *TestHelper) CreatePostReply(root *model.Post) *model.Post { if err != nil { panic(err) } - if post, err = th.App.CreatePost(th.Context, post, ch, model.CreatePostFlags{SetOnline: true}); err != nil { + if post, _, err = th.App.CreatePost(th.Context, post, ch, model.CreatePostFlags{SetOnline: true}); err != nil { panic(err) } return post @@ -861,7 +861,7 @@ func (th *TestHelper) PostPatch(post *model.Post, message string, options ...Pos optionFunc(postPatch) } - updatedPost, appErr := th.App.PatchPost(th.Context, post.Id, postPatch, nil) + updatedPost, _, appErr := th.App.PatchPost(th.Context, post.Id, postPatch, nil) if appErr != nil { panic(appErr) } diff --git a/server/channels/app/integration_action.go b/server/channels/app/integration_action.go index 728a254af4..d7594db324 100644 --- a/server/channels/app/integration_action.go +++ b/server/channels/app/integration_action.go @@ -280,7 +280,7 @@ func (a *App) DoPostActionWithCookie(c request.CTX, postID, actionId, userID, se response.Update.IsPinned = originalIsPinned response.Update.HasReactions = originalHasReactions - if _, appErr = a.UpdatePost(c, response.Update, &model.UpdatePostOptions{SafeUpdate: false}); appErr != nil { + if _, _, appErr = a.UpdatePost(c, response.Update, &model.UpdatePostOptions{SafeUpdate: false}); appErr != nil { return "", appErr } } diff --git a/server/channels/app/integration_action_test.go b/server/channels/app/integration_action_test.go index 535065d652..c9d543faf5 100644 --- a/server/channels/app/integration_action_test.go +++ b/server/channels/app/integration_action_test.go @@ -59,7 +59,7 @@ func TestPostActionInvalidURL(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) require.True(t, ok) @@ -113,7 +113,7 @@ func TestPostActionEmptyResponse(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) @@ -157,7 +157,7 @@ func TestPostActionEmptyResponse(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) @@ -268,7 +268,7 @@ func TestPostAction(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) @@ -305,7 +305,7 @@ func TestPostAction(t *testing.T) { }, } - post2, err := th.App.CreatePostAsUser(th.Context, &menuPost, "", true) + post2, _, err := th.App.CreatePostAsUser(th.Context, &menuPost, "", true) require.Nil(t, err) attachments2, ok := post2.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) @@ -363,7 +363,7 @@ func TestPostAction(t *testing.T) { }, } - postplugin, err := th.App.CreatePostAsUser(th.Context, &interactivePostPlugin, "", true) + postplugin, _, err := th.App.CreatePostAsUser(th.Context, &interactivePostPlugin, "", true) require.Nil(t, err) attachmentsPlugin, ok := postplugin.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) @@ -411,7 +411,7 @@ func TestPostAction(t *testing.T) { }, } - postSiteURL, err := th.App.CreatePostAsUser(th.Context, &interactivePostSiteURL, "", true) + postSiteURL, _, err := th.App.CreatePostAsUser(th.Context, &interactivePostSiteURL, "", true) require.Nil(t, err) attachmentsSiteURL, ok := postSiteURL.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) @@ -453,7 +453,7 @@ func TestPostAction(t *testing.T) { }, } - postSubpath, err := th.App.CreatePostAsUser(th.Context, &interactivePostSubpath, "", true) + postSubpath, _, err := th.App.CreatePostAsUser(th.Context, &interactivePostSubpath, "", true) require.Nil(t, err) attachmentsSubpath, ok := postSubpath.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) @@ -529,7 +529,7 @@ func TestPostActionProps(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) require.True(t, ok) @@ -715,7 +715,7 @@ func TestPostActionRelativeURL(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) require.True(t, ok) @@ -755,7 +755,7 @@ func TestPostActionRelativeURL(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) require.True(t, ok) @@ -795,7 +795,7 @@ func TestPostActionRelativeURL(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) require.True(t, ok) @@ -835,7 +835,7 @@ func TestPostActionRelativeURL(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) require.True(t, ok) @@ -875,7 +875,7 @@ func TestPostActionRelativeURL(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) require.True(t, ok) @@ -953,7 +953,7 @@ func TestPostActionRelativePluginURL(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) require.True(t, ok) @@ -993,7 +993,7 @@ func TestPostActionRelativePluginURL(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) require.True(t, ok) @@ -1033,7 +1033,7 @@ func TestPostActionRelativePluginURL(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) require.True(t, ok) @@ -1073,7 +1073,7 @@ func TestPostActionRelativePluginURL(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) + post, _, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true) require.Nil(t, err) attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment) require.True(t, ok) diff --git a/server/channels/app/notification.go b/server/channels/app/notification.go index 0927820acd..733c0a1d75 100644 --- a/server/channels/app/notification.go +++ b/server/channels/app/notification.go @@ -825,7 +825,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea a.sanitizeProfiles(userThread.Participants, false) userThread.Post.SanitizeProps() - sanitizedPost, err := a.SanitizePostMetadataForUser(c, userThread.Post, uid) + sanitizedPost, isMemberForPreview, err := a.SanitizePostMetadataForUser(c, userThread.Post, uid) if err != nil { a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonParseError, model.NotificationNoPlatform) a.NotificationsLog().Error("Failed to sanitize metadata", @@ -849,6 +849,18 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea message.Add("previous_unread_mentions", previousUnreadMentions) message.Add("previous_unread_replies", previousUnreadReplies) + auditRec := a.MakeAuditRecord(c, "websocketPost", model.AuditStatusSuccess) + defer a.LogAuditRec(c, auditRec, nil) + model.AddEventParameterToAuditRec(auditRec, "post_id", userThread.Post.Id) + if !isMemberForPreview { + previewPost := userThread.Post.GetPreviewPost() + if previewPost != nil { + model.AddEventParameterToAuditRec(auditRec, "preview_post_id", previewPost.Post.Id) + } + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + auditRec.Success() + a.Publish(message) } } @@ -1008,7 +1020,7 @@ func (a *App) RemoveNotifications(c request.CTX, post *model.Post, channel *mode a.sanitizeProfiles(userThread.Participants, false) userThread.Post.SanitizeProps() - sanitizedPost, err1 := a.SanitizePostMetadataForUser(c, userThread.Post, userID) + sanitizedPost, isMemberForPreview, err1 := a.SanitizePostMetadataForUser(c, userThread.Post, userID) if err1 != nil { return err1 } @@ -1019,6 +1031,18 @@ func (a *App) RemoveNotifications(c request.CTX, post *model.Post, channel *mode c.Logger().Warn("Failed to encode thread to JSON") } + auditRec := a.MakeAuditRecord(c, "websocketPost", model.AuditStatusSuccess) + defer a.LogAuditRec(c, auditRec, nil) + model.AddEventParameterToAuditRec(auditRec, "post_id", userThread.Post.Id) + if !isMemberForPreview { + previewPost := userThread.Post.GetPreviewPost() + if previewPost != nil { + model.AddEventParameterToAuditRec(auditRec, "preview_post_id", previewPost.Post.Id) + } + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + auditRec.Success() + message := model.NewWebSocketEvent(model.WebsocketEventThreadUpdated, team.Id, "", userID, nil, "") message.Add("thread", string(payload)) message.Add("previous_unread_mentions", previousUnreadMentions) @@ -1451,7 +1475,7 @@ func getMentionsEnabledFields(post *model.Post) model.StringArray { // allowChannelMentions returns whether or not the channel mentions are allowed for the given post. func (a *App) allowChannelMentions(c request.CTX, post *model.Post, numProfiles int) bool { - if !a.HasPermissionToChannel(c, post.UserId, post.ChannelId, model.PermissionUseChannelMentions) { + if ok, _ := a.HasPermissionToChannel(c, post.UserId, post.ChannelId, model.PermissionUseChannelMentions); !ok { return false } @@ -1472,7 +1496,7 @@ func (a *App) allowGroupMentions(c request.CTX, post *model.Post) bool { return false } - if !a.HasPermissionToChannel(c, post.UserId, post.ChannelId, model.PermissionUseGroupMentions) { + if ok, _ := a.HasPermissionToChannel(c, post.UserId, post.ChannelId, model.PermissionUseGroupMentions); !ok { return false } diff --git a/server/channels/app/notification_test.go b/server/channels/app/notification_test.go index 7569a3038b..4f14a203e5 100644 --- a/server/channels/app/notification_test.go +++ b/server/channels/app/notification_test.go @@ -45,7 +45,7 @@ func TestSendNotifications(t *testing.T) { _, appErr := th.App.AddUserToChannel(th.Context, th.BasicUser2, th.BasicChannel, false) require.Nil(t, appErr) - post1, createPostErr := th.App.CreatePostMissingChannel(th.Context, &model.Post{ + post1, _, createPostErr := th.App.CreatePostMissingChannel(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "@" + th.BasicUser2.Username, @@ -76,7 +76,7 @@ func TestSendNotifications(t *testing.T) { Message: fmt.Sprintf("hello @%s group", *group.Name), CreateAt: model.GetMillis() - 10000, } - groupMentionPost, createPostErr := th.App.CreatePost(th.Context, groupMentionPost, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + groupMentionPost, _, createPostErr := th.App.CreatePost(th.Context, groupMentionPost, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, createPostErr) mentions, err := th.App.SendNotifications(th.Context, groupMentionPost, th.BasicTeam, th.BasicChannel, th.BasicUser, nil, true) @@ -96,7 +96,7 @@ func TestSendNotifications(t *testing.T) { dm, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id) require.Nil(t, appErr) - post2, appErr := th.App.CreatePostMissingChannel(th.Context, &model.Post{ + post2, _, appErr := th.App.CreatePostMissingChannel(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: dm.Id, Message: "dm message", @@ -112,7 +112,7 @@ func TestSendNotifications(t *testing.T) { appErr = th.App.Srv().InvalidateAllCaches() require.Nil(t, appErr) - post3, appErr := th.App.CreatePostMissingChannel(th.Context, &model.Post{ + post3, _, appErr := th.App.CreatePostMissingChannel(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: dm.Id, Message: "dm message", @@ -137,7 +137,7 @@ func TestSendNotifications(t *testing.T) { } channel := th.CreateGroupChannel(th.Context, users[0], users[1]) - post2, appErr := th.App.CreatePostMissingChannel(th.Context, &model.Post{ + post2, _, appErr := th.App.CreatePostMissingChannel(th.Context, &model.Post{ UserId: users[0].Id, ChannelId: channel.Id, Message: "gm message", @@ -153,7 +153,7 @@ func TestSendNotifications(t *testing.T) { appErr = th.App.Srv().InvalidateAllCaches() require.Nil(t, appErr) - post3, appErr := th.App.CreatePostMissingChannel(th.Context, &model.Post{ + post3, _, appErr := th.App.CreatePostMissingChannel(th.Context, &model.Post{ UserId: users[0].Id, ChannelId: channel.Id, Message: "gm message", @@ -179,7 +179,7 @@ func TestSendNotifications(t *testing.T) { Props: model.StringInterface{model.PostPropsFromWebhook: "true", model.PostPropsOverrideUsername: "a bot"}, } - rootPost, appErr := th.App.CreatePostMissingChannel(th.Context, rootPost, false, true) + rootPost, _, appErr := th.App.CreatePostMissingChannel(th.Context, rootPost, false, true) require.Nil(t, appErr) childPost := &model.Post{ @@ -188,7 +188,7 @@ func TestSendNotifications(t *testing.T) { RootId: rootPost.Id, Message: "a reply", } - childPost, appErr = th.App.CreatePostMissingChannel(th.Context, childPost, false, true) + childPost, _, appErr = th.App.CreatePostMissingChannel(th.Context, childPost, false, true) require.Nil(t, appErr) postList := model.PostList{ @@ -361,7 +361,7 @@ func TestSendNotifications_MentionsFollowers(t *testing.T) { } // Use CreatePost instead of SendNotifications here since we need that to set up some threads state - _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) received1 := <-messages1 @@ -580,7 +580,7 @@ func TestSendNotificationsWithManyUsers(t *testing.T) { users = append(users, user) } - _, appErr1 := th.App.CreatePostMissingChannel(th.Context, &model.Post{ + _, _, appErr1 := th.App.CreatePostMissingChannel(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "@channel", @@ -600,7 +600,7 @@ func TestSendNotificationsWithManyUsers(t *testing.T) { } }) - _, appErr1 = th.App.CreatePostMissingChannel(th.Context, &model.Post{ + _, _, appErr1 = th.App.CreatePostMissingChannel(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "@channel", @@ -2860,7 +2860,7 @@ func TestReplyPostNotificationsWithCRT(t *testing.T) { Message: "root post by user1", UserId: u1.Id, } - rpost, appErr := th.App.CreatePost(th.Context, rootPost, c1, model.CreatePostFlags{SetOnline: true}) + rpost, _, appErr := th.App.CreatePost(th.Context, rootPost, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) replyPost1 := &model.Post{ @@ -2869,7 +2869,7 @@ func TestReplyPostNotificationsWithCRT(t *testing.T) { UserId: u2.Id, RootId: rpost.Id, } - _, appErr = th.App.CreatePost(th.Context, replyPost1, c1, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th.App.CreatePost(th.Context, replyPost1, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) replyPost2 := &model.Post{ @@ -2878,7 +2878,7 @@ func TestReplyPostNotificationsWithCRT(t *testing.T) { UserId: u1.Id, RootId: rpost.Id, } - _, appErr = th.App.CreatePost(th.Context, replyPost2, c1, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th.App.CreatePost(th.Context, replyPost2, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) threadMembership, appErr := th.App.GetThreadMembershipForUser(u2.Id, rpost.Id) @@ -2910,7 +2910,7 @@ func TestReplyPostNotificationsWithCRT(t *testing.T) { Props: model.StringInterface{model.PostPropsFromWebhook: "true", model.PostPropsOverrideUsername: "a bot"}, } - rootPost, appErr := th.App.CreatePostMissingChannel(th.Context, rootPost, false, true) + rootPost, _, appErr := th.App.CreatePostMissingChannel(th.Context, rootPost, false, true) require.Nil(t, appErr) childPost := &model.Post{ @@ -2919,7 +2919,7 @@ func TestReplyPostNotificationsWithCRT(t *testing.T) { RootId: rootPost.Id, Message: "a reply", } - childPost, appErr = th.App.CreatePostMissingChannel(th.Context, childPost, false, true) + childPost, _, appErr = th.App.CreatePostMissingChannel(th.Context, childPost, false, true) require.Nil(t, appErr) postList := model.PostList{ @@ -2955,7 +2955,7 @@ func TestReplyPostNotificationsWithCRT(t *testing.T) { Message: "root post by user1", UserId: u1.Id, } - rpost, appErr := th.App.CreatePost(th.Context, rootPost, c1, model.CreatePostFlags{SetOnline: true}) + rpost, _, appErr := th.App.CreatePost(th.Context, rootPost, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) // Remove user1 from the channel @@ -2968,7 +2968,7 @@ func TestReplyPostNotificationsWithCRT(t *testing.T) { UserId: u2.Id, RootId: rpost.Id, } - _, appErr = th.App.CreatePost(th.Context, replyPost, c1, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th.App.CreatePost(th.Context, replyPost, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) // Ensure user1 is not auto-following the thread @@ -3001,7 +3001,7 @@ func TestChannelAutoFollowThreads(t *testing.T) { Message: "root post by user3", UserId: u3.Id, } - rpost, appErr := th.App.CreatePost(th.Context, rootPost, c1, model.CreatePostFlags{SetOnline: true}) + rpost, _, appErr := th.App.CreatePost(th.Context, rootPost, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) replyPost1 := &model.Post{ @@ -3010,7 +3010,7 @@ func TestChannelAutoFollowThreads(t *testing.T) { UserId: u1.Id, RootId: rpost.Id, } - _, appErr = th.App.CreatePost(th.Context, replyPost1, c1, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th.App.CreatePost(th.Context, replyPost1, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) // user-2 starts auto-following thread @@ -3032,7 +3032,7 @@ func TestChannelAutoFollowThreads(t *testing.T) { UserId: u1.Id, RootId: rpost.Id, } - _, appErr = th.App.CreatePost(th.Context, replyPost2, c1, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th.App.CreatePost(th.Context, replyPost2, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) // Do NOT start auto-following thread, once "un-followed" @@ -3064,7 +3064,7 @@ func TestRemoveNotifications(t *testing.T) { Message: "root post by user1", UserId: u1.Id, } - rootPost, appErr := th.App.CreatePost(th.Context, rootPost, c1, model.CreatePostFlags{SetOnline: true}) + rootPost, _, appErr := th.App.CreatePost(th.Context, rootPost, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) replyPost1 := &model.Post{ @@ -3073,7 +3073,7 @@ func TestRemoveNotifications(t *testing.T) { UserId: u2.Id, RootId: rootPost.Id, } - _, appErr = th.App.CreatePost(th.Context, replyPost1, c1, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th.App.CreatePost(th.Context, replyPost1, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) replyPost2 := &model.Post{ @@ -3082,7 +3082,7 @@ func TestRemoveNotifications(t *testing.T) { UserId: u1.Id, RootId: rootPost.Id, } - replyPost2, appErr = th.App.CreatePost(th.Context, replyPost2, c1, model.CreatePostFlags{SetOnline: true}) + replyPost2, _, appErr = th.App.CreatePost(th.Context, replyPost2, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) _, appErr = th.App.DeletePost(th.Context, replyPost2.Id, u1.Id) @@ -3121,7 +3121,7 @@ func TestRemoveNotifications(t *testing.T) { Message: "root post by user1", UserId: u1.Id, } - rootPost, appErr = th.App.CreatePost(th.Context, rootPost, c1, model.CreatePostFlags{SetOnline: true}) + rootPost, _, appErr = th.App.CreatePost(th.Context, rootPost, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) replyPost1 := &model.Post{ @@ -3130,7 +3130,7 @@ func TestRemoveNotifications(t *testing.T) { UserId: u2.Id, RootId: rootPost.Id, } - _, appErr = th.App.CreatePost(th.Context, replyPost1, c1, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th.App.CreatePost(th.Context, replyPost1, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) replyPost2 := &model.Post{ @@ -3139,7 +3139,7 @@ func TestRemoveNotifications(t *testing.T) { UserId: u1.Id, RootId: rootPost.Id, } - replyPost2, appErr = th.App.CreatePost(th.Context, replyPost2, c1, model.CreatePostFlags{SetOnline: true}) + replyPost2, _, appErr = th.App.CreatePost(th.Context, replyPost2, c1, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) _, appErr = th.App.DeletePost(th.Context, replyPost2.Id, u1.Id) diff --git a/server/channels/app/notify_admin.go b/server/channels/app/notify_admin.go index 563f68f317..2e6ab1850b 100644 --- a/server/channels/app/notify_admin.go +++ b/server/channels/app/notify_admin.go @@ -164,7 +164,7 @@ func (a *App) upgradePlanAdminNotifyPost(c request.CTX, workspaceName string, us props["trial"] = trial post.SetProps(props) - _, appErr = a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}) if appErr != nil { c.Logger().Warn("Error creating post", mlog.Err(appErr)) diff --git a/server/channels/app/platform/helper_test.go b/server/channels/app/platform/helper_test.go index bd08695913..ff21940e32 100644 --- a/server/channels/app/platform/helper_test.go +++ b/server/channels/app/platform/helper_test.go @@ -61,8 +61,18 @@ func (ms *mockSuite) UserCanSeeOtherUser(c request.CTX, userID string, otherUser return true, nil } -func (ms *mockSuite) HasPermissionToReadChannel(c request.CTX, userID string, channel *model.Channel) bool { - return true +func (ms *mockSuite) HasPermissionToReadChannel(rctx request.CTX, userID string, channel *model.Channel) (bool, bool) { + return true, true +} + +func (ms *mockSuite) MakeAuditRecord(rctx request.CTX, event string, initialStatus string) *model.AuditRecord { + return &model.AuditRecord{ + Status: initialStatus, + EventName: event, + } +} + +func (ms *mockSuite) LogAuditRec(rctx request.CTX, auditRec *model.AuditRecord, err error) { } func setupDBStore(tb testing.TB) (store.Store, *model.SqlSettings) { diff --git a/server/channels/app/platform/mocks/SuiteIFace.go b/server/channels/app/platform/mocks/SuiteIFace.go index 399a8a5fc5..bd3b5db694 100644 --- a/server/channels/app/platform/mocks/SuiteIFace.go +++ b/server/channels/app/platform/mocks/SuiteIFace.go @@ -48,21 +48,56 @@ func (_m *SuiteIFace) GetSession(token string) (*model.Session, *model.AppError) return r0, r1 } -// HasPermissionToReadChannel provides a mock function with given fields: c, userID, channel -func (_m *SuiteIFace) HasPermissionToReadChannel(c request.CTX, userID string, channel *model.Channel) bool { - ret := _m.Called(c, userID, channel) +// HasPermissionToReadChannel provides a mock function with given fields: rctx, userID, channel +func (_m *SuiteIFace) HasPermissionToReadChannel(rctx request.CTX, userID string, channel *model.Channel) (bool, bool) { + ret := _m.Called(rctx, userID, channel) if len(ret) == 0 { panic("no return value specified for HasPermissionToReadChannel") } var r0 bool + var r1 bool + if rf, ok := ret.Get(0).(func(request.CTX, string, *model.Channel) (bool, bool)); ok { + return rf(rctx, userID, channel) + } if rf, ok := ret.Get(0).(func(request.CTX, string, *model.Channel) bool); ok { - r0 = rf(c, userID, channel) + r0 = rf(rctx, userID, channel) } else { r0 = ret.Get(0).(bool) } + if rf, ok := ret.Get(1).(func(request.CTX, string, *model.Channel) bool); ok { + r1 = rf(rctx, userID, channel) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + +// LogAuditRec provides a mock function with given fields: rctx, auditRec, err +func (_m *SuiteIFace) LogAuditRec(rctx request.CTX, auditRec *model.AuditRecord, err error) { + _m.Called(rctx, auditRec, err) +} + +// MakeAuditRecord provides a mock function with given fields: rctx, event, initialStatus +func (_m *SuiteIFace) MakeAuditRecord(rctx request.CTX, event string, initialStatus string) *model.AuditRecord { + ret := _m.Called(rctx, event, initialStatus) + + if len(ret) == 0 { + panic("no return value specified for MakeAuditRecord") + } + + var r0 *model.AuditRecord + if rf, ok := ret.Get(0).(func(request.CTX, string, string) *model.AuditRecord); ok { + r0 = rf(rctx, event, initialStatus) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.AuditRecord) + } + } + return r0 } @@ -84,9 +119,9 @@ func (_m *SuiteIFace) RolesGrantPermission(roleNames []string, permissionId stri return r0 } -// UserCanSeeOtherUser provides a mock function with given fields: c, userID, otherUserId -func (_m *SuiteIFace) UserCanSeeOtherUser(c request.CTX, userID string, otherUserId string) (bool, *model.AppError) { - ret := _m.Called(c, userID, otherUserId) +// UserCanSeeOtherUser provides a mock function with given fields: rctx, userID, otherUserId +func (_m *SuiteIFace) UserCanSeeOtherUser(rctx request.CTX, userID string, otherUserId string) (bool, *model.AppError) { + ret := _m.Called(rctx, userID, otherUserId) if len(ret) == 0 { panic("no return value specified for UserCanSeeOtherUser") @@ -95,16 +130,16 @@ func (_m *SuiteIFace) UserCanSeeOtherUser(c request.CTX, userID string, otherUse var r0 bool var r1 *model.AppError if rf, ok := ret.Get(0).(func(request.CTX, string, string) (bool, *model.AppError)); ok { - return rf(c, userID, otherUserId) + return rf(rctx, userID, otherUserId) } if rf, ok := ret.Get(0).(func(request.CTX, string, string) bool); ok { - r0 = rf(c, userID, otherUserId) + r0 = rf(rctx, userID, otherUserId) } else { r0 = ret.Get(0).(bool) } if rf, ok := ret.Get(1).(func(request.CTX, string, string) *model.AppError); ok { - r1 = rf(c, userID, otherUserId) + r1 = rf(rctx, userID, otherUserId) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*model.AppError) diff --git a/server/channels/app/platform/web_hub.go b/server/channels/app/platform/web_hub.go index ccce892bc6..b38924742b 100644 --- a/server/channels/app/platform/web_hub.go +++ b/server/channels/app/platform/web_hub.go @@ -28,8 +28,10 @@ const ( type SuiteIFace interface { GetSession(token string) (*model.Session, *model.AppError) RolesGrantPermission(roleNames []string, permissionId string) bool - HasPermissionToReadChannel(c request.CTX, userID string, channel *model.Channel) bool - UserCanSeeOtherUser(c request.CTX, userID string, otherUserId string) (bool, *model.AppError) + HasPermissionToReadChannel(rctx request.CTX, userID string, channel *model.Channel) (bool, bool) + UserCanSeeOtherUser(rctx request.CTX, userID string, otherUserId string) (bool, *model.AppError) + MakeAuditRecord(rctx request.CTX, event string, initialStatus string) *model.AuditRecord + LogAuditRec(rctx request.CTX, auditRec *model.AuditRecord, err error) } type webConnActivityMessage struct { diff --git a/server/channels/app/plugin_api.go b/server/channels/app/plugin_api.go index 20d369b6e6..f347d5f710 100644 --- a/server/channels/app/plugin_api.go +++ b/server/channels/app/plugin_api.go @@ -591,7 +591,7 @@ func (api *PluginAPI) SearchPostsInTeamForUser(teamID string, userID string, sea includeDeletedChannels = *searchParams.IncludeDeletedChannels } - results, appErr := api.app.SearchPostsForUser(api.ctx, terms, userID, teamID, isOrSearch, includeDeletedChannels, timeZoneOffset, page, perPage) + results, _, appErr := api.app.SearchPostsForUser(api.ctx, terms, userID, teamID, isOrSearch, includeDeletedChannels, timeZoneOffset, page, perPage) if results != nil { results = results.ForPlugin() } @@ -773,7 +773,7 @@ func (api *PluginAPI) DeleteGroupSyncable(groupID string, syncableID string, syn func (api *PluginAPI) CreatePost(post *model.Post) (*model.Post, *model.AppError) { post.AddProp(model.PostPropsFromPlugin, "true") - post, appErr := api.app.CreatePostMissingChannel(api.ctx, post, true, true) + post, _, appErr := api.app.CreatePostMissingChannel(api.ctx, post, true, true) if post != nil { post = post.ForPlugin() } @@ -793,11 +793,13 @@ func (api *PluginAPI) GetReactions(postID string) ([]*model.Reaction, *model.App } func (api *PluginAPI) SendEphemeralPost(userID string, post *model.Post) *model.Post { - return api.app.SendEphemeralPost(api.ctx, userID, post).ForPlugin() + newPost, _ := api.app.SendEphemeralPost(api.ctx, userID, post) + return newPost.ForPlugin() } func (api *PluginAPI) UpdateEphemeralPost(userID string, post *model.Post) *model.Post { - return api.app.UpdateEphemeralPost(api.ctx, userID, post).ForPlugin() + newPost, _ := api.app.UpdateEphemeralPost(api.ctx, userID, post) + return newPost.ForPlugin() } func (api *PluginAPI) DeleteEphemeralPost(userID, postID string) { @@ -858,7 +860,7 @@ func (api *PluginAPI) GetPostsForChannel(channelID string, page, perPage int) (* } func (api *PluginAPI) UpdatePost(post *model.Post) (*model.Post, *model.AppError) { - post, appErr := api.app.UpdatePost(api.ctx, post, &model.UpdatePostOptions{SafeUpdate: false}) + post, _, appErr := api.app.UpdatePost(api.ctx, post, &model.UpdatePostOptions{SafeUpdate: false}) if post != nil { post = post.ForPlugin() } @@ -1104,7 +1106,8 @@ func (api *PluginAPI) HasPermissionToTeam(userID, teamID string, permission *mod } func (api *PluginAPI) HasPermissionToChannel(userID, channelID string, permission *model.Permission) bool { - return api.app.HasPermissionToChannel(api.ctx, userID, channelID, permission) + ok, _ := api.app.HasPermissionToChannel(api.ctx, userID, channelID, permission) + return ok } func (api *PluginAPI) RolesGrantPermission(roleNames []string, permissionId string) bool { diff --git a/server/channels/app/plugin_hooks_test.go b/server/channels/app/plugin_hooks_test.go index aa287580e1..12af6f222e 100644 --- a/server/channels/app/plugin_hooks_test.go +++ b/server/channels/app/plugin_hooks_test.go @@ -112,7 +112,7 @@ func TestHookMessageWillBePosted(t *testing.T) { Message: "message_", CreateAt: model.GetMillis() - 10000, } - _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + _, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) if assert.NotNil(t, err) { assert.Equal(t, "Post rejected by plugin. rejected", err.Message) } @@ -154,7 +154,7 @@ func TestHookMessageWillBePosted(t *testing.T) { Message: "message_", CreateAt: model.GetMillis() - 10000, } - _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + _, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) if assert.NotNil(t, err) { assert.Equal(t, "Post rejected by plugin. rejected", err.Message) } @@ -195,7 +195,7 @@ func TestHookMessageWillBePosted(t *testing.T) { Message: "message", CreateAt: model.GetMillis() - 10000, } - post, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) assert.Equal(t, "message", post.Message) @@ -240,7 +240,7 @@ func TestHookMessageWillBePosted(t *testing.T) { Message: "message", CreateAt: model.GetMillis() - 10000, } - post, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) assert.Equal(t, "message_fromplugin", post.Message) @@ -307,7 +307,7 @@ func TestHookMessageWillBePosted(t *testing.T) { Message: "message", CreateAt: model.GetMillis() - 10000, } - post, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) assert.Equal(t, "prefix_message_suffix", post.Message) }) @@ -353,7 +353,7 @@ func TestHookMessageHasBeenPosted(t *testing.T) { Message: "message", CreateAt: model.GetMillis() - 10000, } - _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + _, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) } @@ -394,11 +394,11 @@ func TestHookMessageWillBeUpdated(t *testing.T) { Message: "message_", CreateAt: model.GetMillis() - 10000, } - post, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) assert.Equal(t, "message_", post.Message) post.Message = post.Message + "edited_" - post, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) + post, _, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) require.Nil(t, err) assert.Equal(t, "message_edited_fromplugin", post.Message) } @@ -444,11 +444,11 @@ func TestHookMessageHasBeenUpdated(t *testing.T) { Message: "message_", CreateAt: model.GetMillis() - 10000, } - post, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) assert.Equal(t, "message_", post.Message) post.Message = post.Message + "edited" - _, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) + _, _, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) require.Nil(t, err) } @@ -492,7 +492,7 @@ func TestHookMessageHasBeenDeleted(t *testing.T) { Message: "message", CreateAt: model.GetMillis() - 10000, } - _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + _, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) _, err = th.App.DeletePost(th.Context, post.Id, th.BasicUser.Id) require.Nil(t, err) @@ -1081,7 +1081,7 @@ func TestHookContext(t *testing.T) { Message: "not this", CreateAt: model.GetMillis() - 10000, } - _, err := th.App.CreatePost(ctx, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + _, _, err := th.App.CreatePost(ctx, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) } @@ -1670,7 +1670,7 @@ func TestHookMessagesWillBeConsumed(t *testing.T) { Message: "message", CreateAt: model.GetMillis() - 10000, } - _, err := th.App.CreatePost(th.Context, newPost, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + _, _, err := th.App.CreatePost(th.Context, newPost, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) post, err := th.App.GetSinglePost(th.Context, newPost.Id, true) @@ -1695,7 +1695,7 @@ func TestHookMessagesWillBeConsumed(t *testing.T) { Message: "message", CreateAt: model.GetMillis() - 10000, } - _, err := th.App.CreatePost(th.Context, newPost, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + _, _, err := th.App.CreatePost(th.Context, newPost, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) post, err := th.App.GetSinglePost(th.Context, newPost.Id, true) diff --git a/server/channels/app/plugin_test.go b/server/channels/app/plugin_test.go index e630674f43..cd9f430bae 100644 --- a/server/channels/app/plugin_test.go +++ b/server/channels/app/plugin_test.go @@ -765,7 +765,7 @@ func TestPluginPanicLogs(t *testing.T) { Message: "message_", CreateAt: model.GetMillis() - 10000, } - _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + _, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) assert.Nil(t, err) th.TestLogger.Flush() diff --git a/server/channels/app/post.go b/server/channels/app/post.go index 90c8c481b4..56f5e4daf1 100644 --- a/server/channels/app/post.go +++ b/server/channels/app/post.go @@ -37,32 +37,32 @@ const ( var atMentionPattern = regexp.MustCompile(`\B@`) -func (a *App) CreatePostAsUser(c request.CTX, post *model.Post, currentSessionId string, setOnline bool) (*model.Post, *model.AppError) { +func (a *App) CreatePostAsUser(c request.CTX, post *model.Post, currentSessionId string, setOnline bool) (*model.Post, bool, *model.AppError) { // Check that channel has not been deleted channel, errCh := a.Srv().Store().Channel().Get(post.ChannelId, true) if errCh != nil { err := model.NewAppError("CreatePostAsUser", "api.context.invalid_param.app_error", map[string]any{"Name": "post.channel_id"}, "", http.StatusBadRequest).Wrap(errCh) - return nil, err + return nil, false, err } if strings.HasPrefix(post.Type, model.PostSystemMessagePrefix) { err := model.NewAppError("CreatePostAsUser", "api.context.invalid_param.app_error", map[string]any{"Name": "post.type"}, "", http.StatusBadRequest) - return nil, err + return nil, false, err } if channel.DeleteAt != 0 { err := model.NewAppError("createPost", "api.post.create_post.can_not_post_to_deleted.error", nil, "", http.StatusBadRequest) - return nil, err + return nil, false, err } - rp, err := a.CreatePost(c, post, channel, model.CreatePostFlags{TriggerWebhooks: true, SetOnline: setOnline}) + rp, isMemberForPreviews, err := a.CreatePost(c, post, channel, model.CreatePostFlags{TriggerWebhooks: true, SetOnline: setOnline}) if err != nil { if err.Id == "api.post.create_post.root_id.app_error" || err.Id == "api.post.create_post.channel_root_id.app_error" { err.StatusCode = http.StatusBadRequest } - return nil, err + return nil, false, err } // Update the Channel LastViewAt only if: @@ -115,19 +115,19 @@ func (a *App) CreatePostAsUser(c request.CTX, post *model.Post, currentSessionId } } - return rp, nil + return rp, isMemberForPreviews, nil } -func (a *App) CreatePostMissingChannel(c request.CTX, post *model.Post, triggerWebhooks bool, setOnline bool) (*model.Post, *model.AppError) { +func (a *App) CreatePostMissingChannel(c request.CTX, post *model.Post, triggerWebhooks bool, setOnline bool) (*model.Post, bool, *model.AppError) { channel, err := a.Srv().Store().Channel().Get(post.ChannelId, true) if err != nil { errCtx := map[string]any{"channel_id": post.ChannelId} var nfErr *store.ErrNotFound switch { case errors.As(err, &nfErr): - return nil, model.NewAppError("CreatePostMissingChannel", "app.channel.get.existing.app_error", errCtx, "", http.StatusNotFound).Wrap(err) + return nil, false, model.NewAppError("CreatePostMissingChannel", "app.channel.get.existing.app_error", errCtx, "", http.StatusNotFound).Wrap(err) default: - return nil, model.NewAppError("CreatePostMissingChannel", "app.channel.get.find.app_error", errCtx, "", http.StatusInternalServerError).Wrap(err) + return nil, false, model.NewAppError("CreatePostMissingChannel", "app.channel.get.find.app_error", errCtx, "", http.StatusInternalServerError).Wrap(err) } } @@ -168,7 +168,7 @@ func (a *App) deduplicateCreatePost(rctx request.CTX, post *model.Post) (foundPo // If the other thread finished creating the post, return the created post back to the // client, making the API call feel idempotent. - actualPost, err := a.GetPostIfAuthorized(rctx, postID, rctx.Session(), false) + actualPost, err, _ := a.GetPostIfAuthorized(rctx, postID, rctx.Session(), false) if err != nil && err.StatusCode == http.StatusForbidden { rctx.Logger().Warn("Ignoring pending_post_id for which the user is unauthorized", mlog.String("pending_post_id", post.PendingPostId), mlog.String("post_id", postID), mlog.Err(err)) return nil, nil @@ -181,17 +181,25 @@ func (a *App) deduplicateCreatePost(rctx request.CTX, post *model.Post) (foundPo return actualPost, nil } -func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel, flags model.CreatePostFlags) (savedPost *model.Post, err *model.AppError) { +func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel, flags model.CreatePostFlags) (savedPost *model.Post, isMemberForPreviews bool, err *model.AppError) { if !a.Config().FeatureFlags.EnableSharedChannelsDMs && channel.IsShared() && (channel.Type == model.ChannelTypeDirect || channel.Type == model.ChannelTypeGroup) { - return nil, model.NewAppError("CreatePost", "app.post.create_post.shared_dm_or_gm.app_error", nil, "", http.StatusBadRequest) + return nil, false, model.NewAppError("CreatePost", "app.post.create_post.shared_dm_or_gm.app_error", nil, "", http.StatusBadRequest) } foundPost, err := a.deduplicateCreatePost(c, post) if err != nil { - return nil, err + return nil, false, err } if foundPost != nil { - return foundPost, nil + isMemberForPreviews = true + if previewPost := foundPost.GetPreviewPost(); previewPost != nil { + var member *model.ChannelMember + member, err = a.GetChannelMember(c, previewPost.Post.ChannelId, c.Session().UserId) + if err != nil || member == nil { + isMemberForPreviews = false + } + } + return foundPost, isMemberForPreviews, nil } // If we get this far, we've recorded the client-provided pending post id to the cache. @@ -224,7 +232,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel return nil }) if err != nil { - return nil, model.NewAppError("CreatePost", "api.post.post_priority.persistent_notification_validation_error.request_error", nil, "", http.StatusInternalServerError).Wrap(err) + return nil, false, model.NewAppError("CreatePost", "api.post.post_priority.persistent_notification_validation_error.request_error", nil, "", http.StatusInternalServerError).Wrap(err) } } @@ -245,9 +253,9 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel var nfErr *store.ErrNotFound switch { case errors.As(nErr, &nfErr): - return nil, model.NewAppError("CreatePost", MissingAccountError, nil, "", http.StatusNotFound).Wrap(nErr) + return nil, false, model.NewAppError("CreatePost", MissingAccountError, nil, "", http.StatusNotFound).Wrap(nErr) default: - return nil, model.NewAppError("CreatePost", "app.user.get.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) + return nil, false, model.NewAppError("CreatePost", "app.user.get.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) } } @@ -264,16 +272,18 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel } var ephemeralPost *model.Post - if post.Type == "" && !a.HasPermissionToChannel(c, user.Id, channel.Id, model.PermissionUseChannelMentions) { - mention := post.DisableMentionHighlights() - if mention != "" { - T := i18n.GetUserTranslations(user.Locale) - ephemeralPost = &model.Post{ - UserId: user.Id, - RootId: post.RootId, - ChannelId: channel.Id, - Message: T("model.post.channel_notifications_disabled_in_channel.message", model.StringInterface{"ChannelName": channel.Name, "Mention": mention}), - Props: model.StringInterface{model.PostPropsMentionHighlightDisabled: true}, + if post.Type == "" { + if hasPermission, _ := a.HasPermissionToChannel(c, user.Id, channel.Id, model.PermissionUseChannelMentions); !hasPermission { + mention := post.DisableMentionHighlights() + if mention != "" { + T := i18n.GetUserTranslations(user.Locale) + ephemeralPost = &model.Post{ + UserId: user.Id, + RootId: post.RootId, + ChannelId: channel.Id, + Message: T("model.post.channel_notifications_disabled_in_channel.message", model.StringInterface{"ChannelName": channel.Name, "Mention": mention}), + Props: model.StringInterface{model.PostPropsMentionHighlightDisabled: true}, + } } } } @@ -283,23 +293,23 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel if pchan != nil { result := <-pchan if result.NErr != nil { - return nil, model.NewAppError("createPost", "api.post.create_post.root_id.app_error", nil, "", http.StatusBadRequest).Wrap(result.NErr) + return nil, false, model.NewAppError("createPost", "api.post.create_post.root_id.app_error", nil, "", http.StatusBadRequest).Wrap(result.NErr) } parentPostList = result.Data if len(parentPostList.Posts) == 0 || !parentPostList.IsChannelId(post.ChannelId) { - return nil, model.NewAppError("createPost", "api.post.create_post.channel_root_id.app_error", nil, "", http.StatusInternalServerError) + return nil, false, model.NewAppError("createPost", "api.post.create_post.channel_root_id.app_error", nil, "", http.StatusInternalServerError) } rootPost := parentPostList.Posts[post.RootId] if rootPost.RootId != "" { - return nil, model.NewAppError("createPost", "api.post.create_post.root_id.app_error", nil, "", http.StatusBadRequest) + return nil, false, model.NewAppError("createPost", "api.post.create_post.root_id.app_error", nil, "", http.StatusBadRequest) } } post.Hashtags, _ = model.ParseHashtags(post.Message) if err = a.FillInPostProps(c, post, channel); err != nil { - return nil, err + return nil, false, err } // Temporary fix so old plugins don't clobber new fields in SlackAttachment struct, see MM-13088 @@ -344,7 +354,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel }, plugin.MessageWillBePostedID) if rejectionError != nil { - return nil, rejectionError + return nil, false, rejectionError } // Pre-fill the CreateAt field for link previews to get the correct timestamp. @@ -364,18 +374,18 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel var invErr *store.ErrInvalidInput switch { case errors.As(nErr, &appErr): - return nil, appErr + return nil, false, appErr case errors.As(nErr, &invErr): - return nil, model.NewAppError("CreatePost", "app.post.save.existing.app_error", nil, "", http.StatusBadRequest).Wrap(nErr) + return nil, false, model.NewAppError("CreatePost", "app.post.save.existing.app_error", nil, "", http.StatusBadRequest).Wrap(nErr) default: - return nil, model.NewAppError("CreatePost", "app.post.save.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) + return nil, false, model.NewAppError("CreatePost", "app.post.save.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) } } // Update the mapping from pending post id to the actual post id, for any clients that // might be duplicating requests. if appErr := a.Srv().seenPendingPostIdsCache.SetWithExpiry(post.PendingPostId, rpost.Id, pendingPostIDsCacheTTL); appErr != nil { - return nil, model.NewAppError("CreatePost", "api.post.deduplicate_create_post.cache_error", nil, "", http.StatusInternalServerError).Wrap(appErr) + return nil, false, model.NewAppError("CreatePost", "api.post.deduplicate_create_post.cache_error", nil, "", http.StatusInternalServerError).Wrap(appErr) } if a.Metrics() != nil { @@ -420,7 +430,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel mlog.String("reason", model.NotificationReasonResolvePersistentNotificationError), mlog.Err(appErr), ) - return nil, appErr + return nil, false, appErr } } @@ -444,12 +454,12 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel a.SendEphemeralPost(c, post.UserId, ephemeralPost) } - rpost, err = a.SanitizePostMetadataForUser(c, rpost, c.Session().UserId) + rpost, isMemberForPreviews, err = a.SanitizePostMetadataForUser(c, rpost, c.Session().UserId) if err != nil { - return nil, err + return nil, false, err } - return rpost, nil + return rpost, isMemberForPreviews, nil } func (a *App) addPostPreviewProp(rctx request.CTX, post *model.Post) (*model.Post, error) { @@ -515,15 +525,17 @@ func (a *App) FillInPostProps(c request.CTX, post *model.Post, channel *model.Ch } for _, mentioned := range mentionedChannels { - if mentioned.Type == model.ChannelTypeOpen && a.HasPermissionToReadChannel(c, post.UserId, mentioned) { - team, err := a.Srv().Store().Team().Get(mentioned.TeamId) - if err != nil { - c.Logger().Warn("Failed to get team of the channel mention", mlog.String("team_id", channel.TeamId), mlog.String("channel_id", channel.Id), mlog.Err(err)) - continue - } - channelMentionsProp[mentioned.Name] = map[string]any{ - "display_name": mentioned.DisplayName, - "team_name": team.Name, + if mentioned.Type == model.ChannelTypeOpen { + if ok, _ := a.HasPermissionToReadChannel(c, post.UserId, mentioned); ok { + team, err := a.Srv().Store().Team().Get(mentioned.TeamId) + if err != nil { + c.Logger().Warn("Failed to get team of the channel mention", mlog.String("team_id", channel.TeamId), mlog.String("channel_id", channel.Id), mlog.Err(err)) + continue + } + channelMentionsProp[mentioned.Name] = map[string]any{ + "display_name": mentioned.DisplayName, + "team_name": team.Name, + } } } } @@ -536,7 +548,12 @@ func (a *App) FillInPostProps(c request.CTX, post *model.Post, channel *model.Ch } matched := atMentionPattern.MatchString(post.Message) - if a.Srv().License() != nil && *a.Srv().License().Features.LDAPGroups && matched && !a.HasPermissionToChannel(c, post.UserId, post.ChannelId, model.PermissionUseGroupMentions) { + shouldAddProp := false + if a.Srv().License() != nil && *a.Srv().License().Features.LDAPGroups && matched { + hasPermission, _ := a.HasPermissionToChannel(c, post.UserId, post.ChannelId, model.PermissionUseGroupMentions) + shouldAddProp = !hasPermission + } + if shouldAddProp { post.AddProp(model.PostPropsGroupHighlightDisabled, true) } @@ -593,7 +610,7 @@ func (a *App) handlePostEvents(c request.CTX, post *model.Post, user *model.User return nil } -func (a *App) SendEphemeralPost(c request.CTX, userID string, post *model.Post) *model.Post { +func (a *App) SendEphemeralPost(c request.CTX, userID string, post *model.Post) (*model.Post, bool) { post.Type = model.PostTypeEphemeral // fill in fields which haven't been specified which have sensible defaults @@ -612,7 +629,7 @@ func (a *App) SendEphemeralPost(c request.CTX, userID string, post *model.Post) post = a.PreparePostForClientWithEmbedsAndImages(c, post, true, false, true) post = model.AddPostActionCookies(post, a.PostActionCookieSecret()) - sanitizedPost, appErr := a.SanitizePostMetadataForUser(c, post, userID) + sanitizedPost, isMemberForPreviews, appErr := a.SanitizePostMetadataForUser(c, post, userID) if appErr != nil { c.Logger().Error("Failed to sanitize post metadata for user", mlog.String("user_id", userID), mlog.Err(appErr)) @@ -630,10 +647,10 @@ func (a *App) SendEphemeralPost(c request.CTX, userID string, post *model.Post) message.Add("post", postJSON) a.Publish(message) - return post + return post, isMemberForPreviews } -func (a *App) UpdateEphemeralPost(c request.CTX, userID string, post *model.Post) *model.Post { +func (a *App) UpdateEphemeralPost(c request.CTX, userID string, post *model.Post) (*model.Post, bool) { post.Type = model.PostTypeEphemeral post.UpdateAt = model.GetMillis() @@ -646,7 +663,7 @@ func (a *App) UpdateEphemeralPost(c request.CTX, userID string, post *model.Post post = a.PreparePostForClientWithEmbedsAndImages(c, post, true, false, true) post = model.AddPostActionCookies(post, a.PostActionCookieSecret()) - sanitizedPost, appErr := a.SanitizePostMetadataForUser(c, post, userID) + sanitizedPost, isMemberForPreviews, appErr := a.SanitizePostMetadataForUser(c, post, userID) if appErr != nil { c.Logger().Error("Failed to sanitize post metadata for user", mlog.String("user_id", userID), mlog.Err(appErr)) @@ -664,7 +681,7 @@ func (a *App) UpdateEphemeralPost(c request.CTX, userID string, post *model.Post message.Add("post", postJSON) a.Publish(message) - return post + return post, isMemberForPreviews } func (a *App) DeleteEphemeralPost(rctx request.CTX, userID, postID string) { @@ -685,7 +702,7 @@ func (a *App) DeleteEphemeralPost(rctx request.CTX, userID, postID string) { a.Publish(message) } -func (a *App) UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, updatePostOptions *model.UpdatePostOptions) (*model.Post, *model.AppError) { +func (a *App) UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, updatePostOptions *model.UpdatePostOptions) (*model.Post, bool, *model.AppError) { if updatePostOptions == nil { updatePostOptions = model.DefaultUpdatePostOptions() } @@ -698,11 +715,11 @@ func (a *App) UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, updateP var invErr *store.ErrInvalidInput switch { case errors.As(nErr, &invErr): - return nil, model.NewAppError("UpdatePost", "app.post.get.app_error", nil, "", http.StatusBadRequest).Wrap(nErr) + return nil, false, model.NewAppError("UpdatePost", "app.post.get.app_error", nil, "", http.StatusBadRequest).Wrap(nErr) case errors.As(nErr, &nfErr): - return nil, model.NewAppError("UpdatePost", "app.post.get.app_error", nil, "", http.StatusNotFound).Wrap(nErr) + return nil, false, model.NewAppError("UpdatePost", "app.post.get.app_error", nil, "", http.StatusNotFound).Wrap(nErr) default: - return nil, model.NewAppError("UpdatePost", "app.post.get.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) + return nil, false, model.NewAppError("UpdatePost", "app.post.get.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) } } oldPost := postLists.Posts[receivedUpdatedPost.Id] @@ -710,26 +727,26 @@ func (a *App) UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, updateP var appErr *model.AppError if oldPost == nil { appErr = model.NewAppError("UpdatePost", "api.post.update_post.find.app_error", nil, "id="+receivedUpdatedPost.Id, http.StatusBadRequest) - return nil, appErr + return nil, false, appErr } if oldPost.DeleteAt != 0 { appErr = model.NewAppError("UpdatePost", "api.post.update_post.permissions_details.app_error", map[string]any{"PostId": receivedUpdatedPost.Id}, "", http.StatusBadRequest) - return nil, appErr + return nil, false, appErr } if oldPost.IsSystemMessage() { appErr = model.NewAppError("UpdatePost", "api.post.update_post.system_message.app_error", nil, "id="+receivedUpdatedPost.Id, http.StatusBadRequest) - return nil, appErr + return nil, false, appErr } channel, appErr := a.GetChannel(c, oldPost.ChannelId) if appErr != nil { - return nil, appErr + return nil, false, appErr } if channel.DeleteAt != 0 { - return nil, model.NewAppError("UpdatePost", "api.post.update_post.can_not_update_post_in_deleted.error", nil, "", http.StatusBadRequest) + return nil, false, model.NewAppError("UpdatePost", "api.post.update_post.can_not_update_post_in_deleted.error", nil, "", http.StatusBadRequest) } newPost := oldPost.Clone() @@ -748,7 +765,7 @@ func (a *App) UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, updateP var fileIds []string fileIds, appErr = a.processPostFileChanges(c, receivedUpdatedPost, oldPost, updatePostOptions) if appErr != nil { - return nil, appErr + return nil, false, appErr } newPost.FileIds = fileIds } @@ -759,7 +776,7 @@ func (a *App) UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, updateP } if appErr = a.FillInPostProps(c, newPost, nil); appErr != nil { - return nil, appErr + return nil, false, appErr } if receivedUpdatedPost.IsRemote() { @@ -773,7 +790,7 @@ func (a *App) UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, updateP return newPost != nil }, plugin.MessageWillBeUpdatedID) if newPost == nil { - return nil, model.NewAppError("UpdatePost", "Post rejected by plugin. "+rejectionReason, nil, "", http.StatusBadRequest) + return nil, false, model.NewAppError("UpdatePost", "Post rejected by plugin. "+rejectionReason, nil, "", http.StatusBadRequest) } // Always use incoming metadata when provided, otherwise retain existing if receivedUpdatedPost.Metadata != nil { @@ -788,9 +805,9 @@ func (a *App) UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, updateP if nErr != nil { switch { case errors.As(nErr, &appErr): - return nil, appErr + return nil, false, appErr default: - return nil, model.NewAppError("UpdatePost", "app.post.update.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) + return nil, false, model.NewAppError("UpdatePost", "app.post.update.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) } } @@ -812,20 +829,20 @@ func (a *App) UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, updateP rpost, nErr = a.addPostPreviewProp(c, rpost) if nErr != nil { - return nil, model.NewAppError("UpdatePost", "app.post.update.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) + return nil, false, model.NewAppError("UpdatePost", "app.post.update.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) } message := model.NewWebSocketEvent(model.WebsocketEventPostEdited, "", rpost.ChannelId, "", nil, "") appErr = a.publishWebsocketEventForPost(c, rpost, message) if appErr != nil { - return nil, appErr + return nil, false, appErr } a.invalidateCacheForChannelPosts(rpost.ChannelId) userID := c.Session().UserId - sanitizedPost, appErr := a.SanitizePostMetadataForUser(c, rpost, userID) + sanitizedPost, isMemberForPreviews, appErr := a.SanitizePostMetadataForUser(c, rpost, userID) if appErr != nil { mlog.Error("Failed to sanitize post metadata for user", mlog.String("user_id", userID), mlog.Err(appErr)) @@ -836,7 +853,7 @@ func (a *App) UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, updateP } rpost = sanitizedPost - return rpost, nil + return rpost, isMemberForPreviews, nil } func (a *App) publishWebsocketEventForPost(rctx request.CTX, post *model.Post, message *model.WebSocketEvent) *model.AppError { @@ -945,7 +962,9 @@ func (a *App) setupBroadcastHookForPermalink(rctx request.CTX, post *model.Post, // In case the user does have permission to read, we set the metadata back. // Note that this is the return value to the post creator, and has nothing to do // with the content of the websocket broadcast to that user or any other. - if a.HasPermissionToReadChannel(rctx, post.UserId, permalinkPreviewedChannel) { + // We also don't check the membership for the previewed post, since + // the broadcast handler will create the audit events if needed. + if ok, _ := a.HasPermissionToReadChannel(rctx, post.UserId, permalinkPreviewedChannel); ok { post.AddProp(model.PostPropsPreviewedPost, previewProp) post.Metadata.Embeds = append(post.Metadata.Embeds, &model.PostEmbed{Type: model.PostEmbedPermalink, Data: permalinkPreviewedPost}) } @@ -954,39 +973,39 @@ func (a *App) setupBroadcastHookForPermalink(rctx request.CTX, post *model.Post, return nil } -func (a *App) PatchPost(c request.CTX, postID string, patch *model.PostPatch, patchPostOptions *model.UpdatePostOptions) (*model.Post, *model.AppError) { +func (a *App) PatchPost(c request.CTX, postID string, patch *model.PostPatch, patchPostOptions *model.UpdatePostOptions) (*model.Post, bool, *model.AppError) { if patchPostOptions == nil { patchPostOptions = model.DefaultUpdatePostOptions() } post, err := a.GetSinglePost(c, postID, false) if err != nil { - return nil, err + return nil, false, err } channel, err := a.GetChannel(c, post.ChannelId) if err != nil { - return nil, err + return nil, false, err } if channel.DeleteAt != 0 { err = model.NewAppError("PatchPost", "api.post.patch_post.can_not_update_post_in_deleted.error", nil, "", http.StatusBadRequest) - return nil, err + return nil, false, err } - if !a.HasPermissionToChannel(c, post.UserId, post.ChannelId, model.PermissionUseChannelMentions) { + if ok, _ := a.HasPermissionToChannel(c, post.UserId, post.ChannelId, model.PermissionUseChannelMentions); !ok { patch.DisableMentionHighlights() } post.Patch(patch) patchPostOptions.SafeUpdate = false - updatedPost, err := a.UpdatePost(c, post, patchPostOptions) + updatedPost, isMemberForPreviews, err := a.UpdatePost(c, post, patchPostOptions) if err != nil { - return nil, err + return nil, false, err } - return updatedPost, nil + return updatedPost, isMemberForPreviews, nil } func (a *App) GetPostsPage(options model.GetPostsOptions) (*model.PostList, *model.AppError) { @@ -1715,13 +1734,13 @@ func (a *App) SearchPostsInTeam(teamID string, paramsList []*model.SearchParams) }) } -func (a *App) SearchPostsForUser(c request.CTX, terms string, userID string, teamID string, isOrSearch bool, includeDeletedChannels bool, timeZoneOffset int, page, perPage int) (*model.PostSearchResults, *model.AppError) { +func (a *App) SearchPostsForUser(c request.CTX, terms string, userID string, teamID string, isOrSearch bool, includeDeletedChannels bool, timeZoneOffset int, page, perPage int) (*model.PostSearchResults, bool, *model.AppError) { var postSearchResults *model.PostSearchResults paramsList := model.ParseSearchParams(strings.TrimSpace(terms), timeZoneOffset) includeDeleted := includeDeletedChannels && *a.Config().TeamSettings.ExperimentalViewArchivedChannels if !*a.Config().ServiceSettings.EnablePostSearch { - return nil, model.NewAppError("SearchPostsForUser", "store.sql_post.search.disabled", nil, fmt.Sprintf("teamId=%v userId=%v", teamID, userID), http.StatusNotImplemented) + return nil, false, model.NewAppError("SearchPostsForUser", "store.sql_post.search.disabled", nil, fmt.Sprintf("teamId=%v userId=%v", teamID, userID), http.StatusNotImplemented) } finalParamsList := []*model.SearchParams{} @@ -1748,7 +1767,7 @@ func (a *App) SearchPostsForUser(c request.CTX, terms string, userID string, tea // If the processed search params are empty, return empty search results. if len(finalParamsList) == 0 { - return model.MakePostSearchResults(model.NewPostList(), nil), nil + return model.MakePostSearchResults(model.NewPostList(), nil), true, nil } postSearchResults, err := a.Srv().Store().Post().SearchPostsForUser(c, finalParamsList, userID, teamID, page, perPage) @@ -1756,26 +1775,27 @@ func (a *App) SearchPostsForUser(c request.CTX, terms string, userID string, tea var appErr *model.AppError switch { case errors.As(err, &appErr): - return nil, appErr + return nil, false, appErr default: - return nil, model.NewAppError("SearchPostsForUser", "app.post.search.app_error", nil, "", http.StatusInternalServerError).Wrap(err) + return nil, false, model.NewAppError("SearchPostsForUser", "app.post.search.app_error", nil, "", http.StatusInternalServerError).Wrap(err) } } if appErr := a.filterInaccessiblePosts(postSearchResults.PostList, filterPostOptions{assumeSortedCreatedAt: true}); appErr != nil { - return nil, appErr + return nil, false, appErr } - if appErr := a.FilterPostsByChannelPermissions(c, postSearchResults.PostList, userID); appErr != nil { - return nil, appErr + allPostHaveMembership, appErr := a.FilterPostsByChannelPermissions(c, postSearchResults.PostList, userID) + if appErr != nil { + return nil, false, appErr } - return postSearchResults, nil + return postSearchResults, allPostHaveMembership, nil } -func (a *App) FilterPostsByChannelPermissions(rctx request.CTX, postList *model.PostList, userID string) *model.AppError { +func (a *App) FilterPostsByChannelPermissions(rctx request.CTX, postList *model.PostList, userID string) (bool, *model.AppError) { if postList == nil || postList.Posts == nil || len(postList.Posts) == 0 { - return nil + return true, nil // On an empty post list, we consider all posts as having membership } channels := make(map[string]*model.Channel) @@ -1789,7 +1809,7 @@ func (a *App) FilterPostsByChannelPermissions(rctx request.CTX, postList *model. channelIDs := slices.Collect(maps.Keys(channels)) channelList, err := a.GetChannels(rctx, channelIDs) if err != nil && err.StatusCode != http.StatusNotFound { - return err + return false, err } for _, channel := range channelList { channels[channel.Id] = channel @@ -1799,6 +1819,7 @@ func (a *App) FilterPostsByChannelPermissions(rctx request.CTX, postList *model. channelReadPermission := make(map[string]bool) filteredPosts := make(map[string]*model.Post) filteredOrder := []string{} + allPostHaveMembership := true for _, postID := range postList.Order { post, ok := postList.Posts[postID] @@ -1809,10 +1830,14 @@ func (a *App) FilterPostsByChannelPermissions(rctx request.CTX, postList *model. if _, ok := channelReadPermission[post.ChannelId]; !ok { channel := channels[post.ChannelId] allowed := false + isMember := true if channel != nil { - allowed = a.HasPermissionToReadChannel(rctx, userID, channel) + allowed, isMember = a.HasPermissionToReadChannel(rctx, userID, channel) } channelReadPermission[post.ChannelId] = allowed + if allowed { + allPostHaveMembership = allPostHaveMembership && isMember + } } if channelReadPermission[post.ChannelId] { @@ -1824,7 +1849,7 @@ func (a *App) FilterPostsByChannelPermissions(rctx request.CTX, postList *model. postList.Posts = filteredPosts postList.Order = filteredOrder - return nil + return allPostHaveMembership, nil } func (a *App) GetFileInfosForPostWithMigration(rctx request.CTX, postID string, includeDeleted bool) ([]*model.FileInfo, *model.AppError) { @@ -2176,28 +2201,29 @@ func (a *App) GetThreadMembershipsForUser(userID, teamID string) ([]*model.Threa return a.Srv().Store().Thread().GetMembershipsForUser(userID, teamID) } -func (a *App) GetPostIfAuthorized(c request.CTX, postID string, session *model.Session, includeDeleted bool) (*model.Post, *model.AppError) { +func (a *App) GetPostIfAuthorized(c request.CTX, postID string, session *model.Session, includeDeleted bool) (*model.Post, *model.AppError, bool) { post, err := a.GetSinglePost(c, postID, includeDeleted) if err != nil { - return nil, err + return nil, err, false } channel, err := a.GetChannel(c, post.ChannelId) if err != nil { - return nil, err + return nil, err, false } - if !a.SessionHasPermissionToReadChannel(c, *session, channel) { + ok, isMember := a.SessionHasPermissionToReadChannel(c, *session, channel) + if !ok { if channel.Type == model.ChannelTypeOpen && !*a.Config().ComplianceSettings.Enable { if !a.SessionHasPermissionToTeam(*session, channel.TeamId, model.PermissionReadPublicChannel) { - return nil, model.MakePermissionError(session, []*model.Permission{model.PermissionReadPublicChannel}) + return nil, model.MakePermissionError(session, []*model.Permission{model.PermissionReadPublicChannel}), false } } else { - return nil, model.MakePermissionError(session, []*model.Permission{model.PermissionReadChannelContent}) + return nil, model.MakePermissionError(session, []*model.Permission{model.PermissionReadChannelContent}), false } } - return post, nil + return post, nil, isMember } // GetPostsByIds response bool value indicates, if the post is inaccessible due to cloud plan's limit. @@ -2380,7 +2406,7 @@ func (a *App) CheckPostReminders(rctx request.CTX) { }, } - if _, err := a.CreatePost(request.EmptyContext(a.Log()), dm, ch, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(request.EmptyContext(a.Log()), dm, ch, model.CreatePostFlags{SetOnline: true}); err != nil { rctx.Logger().Error("Failed to post reminder message", mlog.Err(err)) } } @@ -2448,9 +2474,9 @@ func (a *App) GetPostInfo(c request.CTX, postID string) (*model.PostInfo, *model if channel.Type == model.ChannelTypeOpen { hasPermissionToAccessChannel = true } else if channel.Type == model.ChannelTypePrivate { - hasPermissionToAccessChannel = a.HasPermissionToChannel(c, userID, channel.Id, model.PermissionManagePrivateChannelMembers) + hasPermissionToAccessChannel, _ = a.HasPermissionToChannel(c, userID, channel.Id, model.PermissionManagePrivateChannelMembers) } else if channel.Type == model.ChannelTypeDirect || channel.Type == model.ChannelTypeGroup { - hasPermissionToAccessChannel = a.HasPermissionToChannel(c, userID, channel.Id, model.PermissionReadChannelContent) + hasPermissionToAccessChannel, _ = a.HasPermissionToChannel(c, userID, channel.Id, model.PermissionReadChannelContent) } } @@ -2567,7 +2593,7 @@ func (a *App) ValidateMoveOrCopy(c request.CTX, wpl *model.WranglerPostList, ori return nil } -func (a *App) CopyWranglerPostlist(c request.CTX, wpl *model.WranglerPostList, targetChannel *model.Channel) (*model.Post, *model.AppError) { +func (a *App) CopyWranglerPostlist(c request.CTX, wpl *model.WranglerPostList, targetChannel *model.Channel) (*model.Post, bool, *model.AppError) { var appErr *model.AppError var newRootPost *model.Post @@ -2587,15 +2613,15 @@ func (a *App) CopyWranglerPostlist(c request.CTX, wpl *model.WranglerPostList, t for _, fileID := range post.FileIds { oldFileInfo, appErr = a.GetFileInfo(c, fileID) if appErr != nil { - return nil, appErr + return nil, false, appErr } fileBytes, appErr = a.GetFile(c, fileID) if appErr != nil { - return nil, appErr + return nil, false, appErr } newFileInfo, appErr = a.UploadFile(c, fileBytes, targetChannel.Id, oldFileInfo.Name) if appErr != nil { - return nil, appErr + return nil, false, appErr } newFileIDs = append(newFileIDs, newFileInfo.Id) @@ -2605,6 +2631,8 @@ func (a *App) CopyWranglerPostlist(c request.CTX, wpl *model.WranglerPostList, t } } + var isMemberForPreviews bool + for i, post := range wpl.Posts { var reactions []*model.Reaction @@ -2620,16 +2648,16 @@ func (a *App) CopyWranglerPostlist(c request.CTX, wpl *model.WranglerPostList, t newPost.ChannelId = targetChannel.Id if i == 0 { - newPost, appErr = a.CreatePost(c, newPost, targetChannel, model.CreatePostFlags{}) + newPost, isMemberForPreviews, appErr = a.CreatePost(c, newPost, targetChannel, model.CreatePostFlags{}) if appErr != nil { - return nil, appErr + return nil, false, appErr } newRootPost = newPost.Clone() } else { newPost.RootId = newRootPost.Id - newPost, appErr = a.CreatePost(c, newPost, targetChannel, model.CreatePostFlags{}) + newPost, _, appErr = a.CreatePost(c, newPost, targetChannel, model.CreatePostFlags{}) if appErr != nil { - return nil, appErr + return nil, false, appErr } } @@ -2643,7 +2671,7 @@ func (a *App) CopyWranglerPostlist(c request.CTX, wpl *model.WranglerPostList, t } } - return newRootPost, nil + return newRootPost, isMemberForPreviews, nil } func (a *App) MoveThread(c request.CTX, postID string, sourceChannelID, channelID string, user *model.User) *model.AppError { @@ -2689,7 +2717,7 @@ func (a *App) MoveThread(c request.CTX, postID string, sourceChannelID, channelI // To simulate the move, we first copy the original messages(s) to the // new channel and later delete the original messages(s). - newRootPost, appErr := a.CopyWranglerPostlist(c, wpl, targetChannel) + newRootPost, _, appErr := a.CopyWranglerPostlist(c, wpl, targetChannel) if appErr != nil { return appErr } @@ -2702,7 +2730,7 @@ func (a *App) MoveThread(c request.CTX, postID string, sourceChannelID, channelI ephemeralPostProps := model.StringInterface{ "TranslationID": "app.post.move_thread.from_another_channel", } - _, appErr = a.CreatePost(c, &model.Post{ + _, _, appErr = a.CreatePost(c, &model.Post{ UserId: user.Id, Type: model.PostTypeWrangler, RootId: newRootPost.Id, @@ -2750,7 +2778,7 @@ func (a *App) MoveThread(c request.CTX, postID string, sourceChannelID, channelI ephemeralPostProps["NumMessages"] = wpl.NumPosts() - _, appErr = a.CreatePost(c, &model.Post{ + _, _, appErr = a.CreatePost(c, &model.Post{ UserId: user.Id, Type: model.PostTypeWrangler, ChannelId: originalChannel.Id, @@ -2871,7 +2899,8 @@ func (a *App) SendTestMessage(c request.CTX, userID string) (*model.Post, *model UserId: bot.UserId, } - post, err = a.CreatePost(c, post, channel, model.CreatePostFlags{ForceNotification: true}) + // We don't check the preview membership because the test message does not send a link to a different post. + post, _, err = a.CreatePost(c, post, channel, model.CreatePostFlags{ForceNotification: true}) if err != nil { return nil, model.NewAppError("SendTestMessage", "app.notifications.send_test_message.errors.create_post", nil, "", http.StatusInternalServerError).Wrap(err) } diff --git a/server/channels/app/post_acknowledgements_test.go b/server/channels/app/post_acknowledgements_test.go index 660b8cec4b..8f97fc0e6d 100644 --- a/server/channels/app/post_acknowledgements_test.go +++ b/server/channels/app/post_acknowledgements_test.go @@ -24,7 +24,7 @@ func testSaveAcknowledgementForPost(t *testing.T) { defer th.TearDown() t.Run("save acknowledgment for post should save acknowledgement", func(t *testing.T) { - post, err := th.App.CreatePostAsUser(th.Context, &model.Post{ + post, _, err := th.App.CreatePostAsUser(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "message", @@ -41,7 +41,7 @@ func testSaveAcknowledgementForPost(t *testing.T) { }) t.Run("saving acknowledgment should update the post's update_at", func(t *testing.T) { - post, err := th.App.CreatePostAsUser(th.Context, &model.Post{ + post, _, err := th.App.CreatePostAsUser(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "message", @@ -65,7 +65,7 @@ func testDeleteAcknowledgementForPost(t *testing.T) { mainHelper.Parallel(t) th := Setup(t).InitBasic() defer th.TearDown() - post, err1 := th.App.CreatePostAsUser(th.Context, &model.Post{ + post, _, err1 := th.App.CreatePostAsUser(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, CreateAt: model.GetMillis(), @@ -138,7 +138,7 @@ func testGetAcknowledgementsForPostList(t *testing.T) { mainHelper.Parallel(t) th := Setup(t).InitBasic() defer th.TearDown() - p1, err := th.App.CreatePostAsUser(th.Context, &model.Post{ + p1, _, err := th.App.CreatePostAsUser(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, CreateAt: model.GetMillis(), @@ -146,7 +146,7 @@ func testGetAcknowledgementsForPostList(t *testing.T) { }, "", true) require.Nil(t, err) - p2, err := th.App.CreatePostAsUser(th.Context, &model.Post{ + p2, _, err := th.App.CreatePostAsUser(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, CreateAt: model.GetMillis(), @@ -154,7 +154,7 @@ func testGetAcknowledgementsForPostList(t *testing.T) { }, "", true) require.Nil(t, err) - p3, err := th.App.CreatePostAsUser(th.Context, &model.Post{ + p3, _, err := th.App.CreatePostAsUser(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, CreateAt: model.GetMillis(), diff --git a/server/channels/app/post_metadata.go b/server/channels/app/post_metadata.go index 50fbd64ece..3e49b88f2d 100644 --- a/server/channels/app/post_metadata.go +++ b/server/channels/app/post_metadata.go @@ -228,50 +228,48 @@ func removeEmbeddedPostsFromMetadata(post *model.Post) { post.Metadata.Embeds = newEmbeds } -func (a *App) sanitizePostMetadataForUserAndChannel(c request.CTX, post *model.Post, previewedPost *model.PreviewPost, previewedChannel *model.Channel, userID string) *model.Post { - if post.Metadata == nil || len(post.Metadata.Embeds) == 0 || previewedPost == nil { - return post - } - - if previewedChannel != nil && !a.HasPermissionToReadChannel(c, userID, previewedChannel) { - removePermalinkMetadataFromPost(post) - } - - return post -} - -func (a *App) SanitizePostMetadataForUser(c request.CTX, post *model.Post, userID string) (*model.Post, *model.AppError) { +func (a *App) SanitizePostMetadataForUser(c request.CTX, post *model.Post, userID string) (*model.Post, bool, *model.AppError) { if post.Metadata == nil || len(post.Metadata.Embeds) == 0 { - return post, nil + return post, true, nil } previewPost := post.GetPreviewPost() if previewPost == nil { - return post, nil + return post, true, nil } previewedChannel, err := a.GetChannel(c, previewPost.Post.ChannelId) if err != nil { - return nil, err + return nil, false, err } - if previewedChannel != nil && !a.HasPermissionToReadChannel(c, userID, previewedChannel) { - removePermalinkMetadataFromPost(post) + isMember := true + + if previewedChannel != nil { + var hasPermission bool + hasPermission, isMember = a.HasPermissionToReadChannel(c, userID, previewedChannel) + if !hasPermission { + removePermalinkMetadataFromPost(post) + // Since we remove the permalink metadata, we return true + isMember = true + } } - return post, nil + return post, isMember, nil } -func (a *App) SanitizePostListMetadataForUser(c request.CTX, postList *model.PostList, userID string) (*model.PostList, *model.AppError) { +func (a *App) SanitizePostListMetadataForUser(c request.CTX, postList *model.PostList, userID string) (*model.PostList, bool, *model.AppError) { clonedPostList := postList.Clone() + allPreviewsHaveMembership := true for postID, post := range clonedPostList.Posts { - sanitizedPost, err := a.SanitizePostMetadataForUser(c, post, userID) + sanitizedPost, isMember, err := a.SanitizePostMetadataForUser(c, post, userID) if err != nil { - return nil, err + return nil, false, err } clonedPostList.Posts[postID] = sanitizedPost + allPreviewsHaveMembership = allPreviewsHaveMembership && isMember } - return clonedPostList, nil + return clonedPostList, allPreviewsHaveMembership, nil } func (a *App) getFileMetadataForPost(rctx request.CTX, post *model.Post, fromMaster bool) ([]*model.FileInfo, int64, *model.AppError) { diff --git a/server/channels/app/post_metadata_test.go b/server/channels/app/post_metadata_test.go index c76d48a5a3..e67fab8b47 100644 --- a/server/channels/app/post_metadata_test.go +++ b/server/channels/app/post_metadata_test.go @@ -194,7 +194,7 @@ func TestPreparePostForClient(t *testing.T) { fileInfo.ChannelId = th.BasicChannel.Id require.Nil(t, err) - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, FileIds: []string{fileInfo.Id}, @@ -222,7 +222,7 @@ func TestPreparePostForClient(t *testing.T) { emoji := th.CreateEmoji() - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: ":" + emoji.Name + ": :taco:", @@ -266,7 +266,7 @@ func TestPreparePostForClient(t *testing.T) { emoji3 := th.CreateEmoji() emoji4 := th.CreateEmoji() - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: ":" + emoji3.Name + ": :taco:", @@ -307,7 +307,7 @@ func TestPreparePostForClient(t *testing.T) { *cfg.ServiceSettings.EnablePostIconOverride = override }) - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "Test", @@ -364,7 +364,7 @@ func TestPreparePostForClient(t *testing.T) { th := setup(t) defer th.TearDown() - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: fmt.Sprintf("This is ![our logo](%s/test-image2.png) and ![our icon](%s/test-image1.png)", server.URL, server.URL), @@ -393,7 +393,7 @@ func TestPreparePostForClient(t *testing.T) { th := setup(t) defer th.TearDown() - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "some post", @@ -426,7 +426,7 @@ func TestPreparePostForClient(t *testing.T) { th := setup(t) defer th.TearDown() - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: `This is our logo: ` + server.URL + `/test-image2.png @@ -462,7 +462,7 @@ func TestPreparePostForClient(t *testing.T) { th := setup(t) defer th.TearDown() - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: `This is our web page: ` + server.URL, @@ -537,7 +537,7 @@ func TestPreparePostForClient(t *testing.T) { } prepost.AddProp(model.PostPropsUnsafeLinks, "true") - post, err := th.App.CreatePost(th.Context, prepost, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, err := th.App.CreatePost(th.Context, prepost, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) clientPost := th.App.PreparePostForClient(th.Context, post, false, false, false) @@ -553,7 +553,7 @@ func TestPreparePostForClient(t *testing.T) { Message: `Bla bla bla: ` + fmt.Sprintf(tc.link, server.URL), } - post, err := th.App.CreatePost(th.Context, prepost, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, err := th.App.CreatePost(th.Context, prepost, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) clientPost := th.App.PreparePostForClient(th.Context, post, false, false, false) @@ -569,7 +569,7 @@ func TestPreparePostForClient(t *testing.T) { th := setup(t) defer th.TearDown() - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Props: map[string]any{ @@ -610,7 +610,7 @@ func TestPreparePostForClient(t *testing.T) { fileInfo, err := th.App.DoUploadFile(th.Context, time.Now(), th.BasicTeam.Id, th.BasicChannel.Id, th.BasicUser.Id, "test.txt", []byte("test"), true) require.Nil(t, err) - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ Message: "test", FileIds: []string{fileInfo.Id}, UserId: th.BasicUser.Id, @@ -645,7 +645,7 @@ func TestPreparePostForClient(t *testing.T) { th.Context.Session().UserId = th.BasicUser.Id - referencedPost, err := th.App.CreatePost(th.Context, &model.Post{ + referencedPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "hello world", @@ -655,7 +655,7 @@ func TestPreparePostForClient(t *testing.T) { link := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id) - previewPost, err := th.App.CreatePost(th.Context, &model.Post{ + previewPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: link, @@ -703,7 +703,7 @@ func TestPreparePostForClient(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.Description, func(t *testing.T) { - referencedPost, err := th.App.CreatePost(th.Context, &model.Post{ + referencedPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: testCase.Channel.Id, Message: "hello world", @@ -713,7 +713,7 @@ func TestPreparePostForClient(t *testing.T) { link := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id) - previewPost, err := th.App.CreatePost(th.Context, &model.Post{ + previewPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: link, @@ -741,7 +741,7 @@ func TestPreparePostForClient(t *testing.T) { th.Context.Session().UserId = th.BasicUser.Id - referencedPost, err := th.App.CreatePost(th.Context, &model.Post{ + referencedPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: `This is our logo: ` + server.URL + `/test-image2.png`, @@ -751,7 +751,7 @@ func TestPreparePostForClient(t *testing.T) { link := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id) - previewPost, err := th.App.CreatePost(th.Context, &model.Post{ + previewPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: link, @@ -778,7 +778,7 @@ func TestPreparePostForClient(t *testing.T) { th.Context.Session().UserId = th.BasicUser.Id - nestedPermalinkPost, err := th.App.CreatePost(th.Context, &model.Post{ + nestedPermalinkPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: `This is our logo: ` + server.URL + `/test-image2.png`, @@ -788,7 +788,7 @@ func TestPreparePostForClient(t *testing.T) { nestedLink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, nestedPermalinkPost.Id) - referencedPost, err := th.App.CreatePost(th.Context, &model.Post{ + referencedPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: nestedLink, @@ -798,7 +798,7 @@ func TestPreparePostForClient(t *testing.T) { link := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id) - previewPost, err := th.App.CreatePost(th.Context, &model.Post{ + previewPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: link, @@ -825,7 +825,7 @@ func TestPreparePostForClient(t *testing.T) { th.Context.Session().UserId = th.BasicUser.Id - referencedPost, err := th.App.CreatePost(th.Context, &model.Post{ + referencedPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "hello world", @@ -834,7 +834,7 @@ func TestPreparePostForClient(t *testing.T) { link := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id) - previewPost, err := th.App.CreatePost(th.Context, &model.Post{ + previewPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: link, @@ -951,7 +951,7 @@ func testProxyOpenGraphImage(t *testing.T, th *TestHelper, shouldProxy bool) { serverURL = server.URL defer server.Close() - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: `This is our web page: ` + server.URL, @@ -2963,131 +2963,6 @@ func TestContainsPermalink(t *testing.T) { } } -func TestSanitizePostMetadataForUserAndChannel(t *testing.T) { - mainHelper.Parallel(t) - th := Setup(t).InitBasic() - defer th.TearDown() - - enableLinkPreviews := *th.App.Config().ServiceSettings.EnableLinkPreviews - siteURL := *th.App.Config().ServiceSettings.SiteURL - defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { - cfg.ServiceSettings.EnableLinkPreviews = &enableLinkPreviews - cfg.ServiceSettings.SiteURL = &siteURL - }) - }() - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.ServiceSettings.EnableLinkPreviews = true - *cfg.ServiceSettings.SiteURL = "http://mymattermost.com" - }) - - t.Run("should not preview for users with no access to the channel", func(t *testing.T) { - directChannel, err := th.App.createDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id) - assert.Nil(t, err) - - userID := model.NewId() - post := &model.Post{ - Id: userID, - Metadata: &model.PostMetadata{ - Embeds: []*model.PostEmbed{ - { - Type: model.PostEmbedPermalink, - Data: &model.PreviewPost{ - PostID: "permalink_post_id", - Post: &model.Post{ - Id: "permalink_post_id", - Message: "permalink post message", - ChannelId: directChannel.Id, - }, - }, - }, - }, - }, - } - - previewedPost := model.NewPreviewPost(post, th.BasicTeam, directChannel) - - actual := th.App.sanitizePostMetadataForUserAndChannel(th.Context, post, previewedPost, directChannel, th.BasicUser2.Id) - assert.NotNil(t, actual.Metadata.Embeds[0].Data) - - guestID := model.NewId() - guest := &model.User{ - Email: "success+" + guestID + "@simulator.amazonses.com", - Username: "un_" + guestID, - Nickname: "nn_" + guestID, - Password: "Password1", - EmailVerified: true, - } - guest, appErr := th.App.CreateGuest(th.Context, guest) - require.Nil(t, appErr) - - actual = th.App.sanitizePostMetadataForUserAndChannel(th.Context, post, previewedPost, directChannel, guest.Id) - assert.Len(t, actual.Metadata.Embeds, 0) - }) - - t.Run("should not preview for archived channels", func(t *testing.T) { - experimentalViewArchivedChannels := *th.App.Config().TeamSettings.ExperimentalViewArchivedChannels - defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { - cfg.TeamSettings.ExperimentalViewArchivedChannels = &experimentalViewArchivedChannels - }) - }() - - publicChannel, err := th.App.CreateChannel(th.Context, &model.Channel{ - Name: model.NewId(), - Type: model.ChannelTypeOpen, - TeamId: th.BasicTeam.Id, - CreatorId: th.SystemAdminUser.Id, - }, true) - - require.Nil(t, err) - require.NotEmpty(t, publicChannel.Id) - - err = th.App.DeleteChannel(th.Context, publicChannel, th.SystemAdminUser.Id) - require.Nil(t, err) - - publicChannel, err = th.App.GetChannel(th.Context, publicChannel.Id) - require.Nil(t, err) - require.NotEmpty(t, publicChannel.Id) - require.NotEqual(t, 0, publicChannel.DeleteAt) - - post := &model.Post{ - Id: th.BasicUser.Id, - Metadata: &model.PostMetadata{ - Embeds: []*model.PostEmbed{ - { - Type: model.PostEmbedPermalink, - Data: &model.PreviewPost{ - PostID: "permalink_post_id", - Post: &model.Post{ - Id: "permalink_post_id", - Message: "permalink post message", - ChannelId: publicChannel.Id, - }, - }, - }, - }, - }, - } - - previewedPost := model.NewPreviewPost(post, th.BasicTeam, publicChannel) - - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.ExperimentalViewArchivedChannels = true - }) - - actual := th.App.sanitizePostMetadataForUserAndChannel(th.Context, post, previewedPost, publicChannel, th.BasicUser.Id) - assert.NotNil(t, actual.Metadata.Embeds[0].Data) - - th.App.UpdateConfig(func(cfg *model.Config) { - *cfg.TeamSettings.ExperimentalViewArchivedChannels = false - }) - - actual = th.App.sanitizePostMetadataForUserAndChannel(th.Context, post, previewedPost, publicChannel, th.BasicUser.Id) - assert.Len(t, actual.Metadata.Embeds, 0) - }) -} - func TestSanitizePostMetaDataForAudit(t *testing.T) { mainHelper.Parallel(t) th := Setup(t).InitBasic() @@ -3099,7 +2974,7 @@ func TestSanitizePostMetaDataForAudit(t *testing.T) { th.Context.Session().UserId = th.BasicUser.Id - referencedPost, err := th.App.CreatePost(th.Context, &model.Post{ + referencedPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "hello world", @@ -3109,7 +2984,7 @@ func TestSanitizePostMetaDataForAudit(t *testing.T) { link := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id) - previewPost, err := th.App.CreatePost(th.Context, &model.Post{ + previewPost, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: link, @@ -3202,15 +3077,16 @@ func TestSanitizePostMetadataForUser(t *testing.T) { }, } - sanitizedPost, err := th.App.SanitizePostMetadataForUser(th.Context, post, th.BasicUser.Id) + sanitizedPost, isMemberForPreviews, err := th.App.SanitizePostMetadataForUser(th.Context, post, th.BasicUser.Id) require.Nil(t, err) require.NotNil(t, sanitizedPost) require.Equal(t, 1, len(sanitizedPost.Metadata.Embeds)) require.Equal(t, model.PostEmbedLink, sanitizedPost.Metadata.Embeds[0].Type) + require.True(t, isMemberForPreviews) }) - t.Run("should remove embeds for archived channels if the config does not allow it", func(t *testing.T) { + t.Run("should not remove embeds for archived channels", func(t *testing.T) { publicChannel, err := th.App.CreateChannel(th.Context, &model.Channel{ Name: model.NewId(), Type: model.ChannelTypeOpen, @@ -3264,7 +3140,7 @@ func TestSanitizePostMetadataForUser(t *testing.T) { *cfg.TeamSettings.ExperimentalViewArchivedChannels = true }) - sanitizedPost, err := th.App.SanitizePostMetadataForUser(th.Context, post, th.BasicUser.Id) + sanitizedPost, _, err := th.App.SanitizePostMetadataForUser(th.Context, post, th.BasicUser.Id) require.Nil(t, err) require.NotNil(t, sanitizedPost) @@ -3275,7 +3151,7 @@ func TestSanitizePostMetadataForUser(t *testing.T) { *cfg.TeamSettings.ExperimentalViewArchivedChannels = false }) - sanitizedPost, err = th.App.SanitizePostMetadataForUser(th.Context, post, th.BasicUser.Id) + sanitizedPost, _, err = th.App.SanitizePostMetadataForUser(th.Context, post, th.BasicUser.Id) require.Nil(t, err) require.NotNil(t, sanitizedPost) diff --git a/server/channels/app/post_permission_utils.go b/server/channels/app/post_permission_utils.go index f51483d91f..743f9a7f84 100644 --- a/server/channels/app/post_permission_utils.go +++ b/server/channels/app/post_permission_utils.go @@ -99,7 +99,7 @@ func postHardenedModeCheck(hardenedModeEnabled, isIntegration bool, props model. func userCreatePostPermissionCheckWithApp(c request.CTX, a *App, userId, channelId string) *model.AppError { hasPermission := false - if a.HasPermissionToChannel(c, userId, channelId, model.PermissionCreatePost) { + if ok, _ := a.HasPermissionToChannel(c, userId, channelId, model.PermissionCreatePost); ok { hasPermission = true } else if channel, err := a.GetChannel(c, channelId); err == nil { // Temporary permission check method until advanced permissions, please do not copy diff --git a/server/channels/app/post_persistent_notification_test.go b/server/channels/app/post_persistent_notification_test.go index 929d55e43b..996f9423a5 100644 --- a/server/channels/app/post_persistent_notification_test.go +++ b/server/channels/app/post_persistent_notification_test.go @@ -219,7 +219,7 @@ func TestSendPersistentNotifications(t *testing.T) { }, }, } - _, appErr = th.App.CreatePost(th.Context, p1, th.BasicChannel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, p1, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) err := th.App.SendPersistentNotifications() @@ -263,7 +263,7 @@ func TestSendPersistentNotificationsBotSender(t *testing.T) { // Simulate old timestamp so persistent notifications are sent right away CreateAt: time.Now().Add(-5 * time.Minute).UnixMilli(), } - post, appErr = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{}) + post, _, appErr = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) assert.EventuallyWithT(t, func(c *assert.CollectT) { @@ -314,7 +314,7 @@ func TestSendPersistentNotificationsBotSenderNotInChannel(t *testing.T) { }, CreateAt: time.Now().Add(-5 * time.Minute).UnixMilli(), } - post, appErr = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, appErr = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) assert.EventuallyWithT(t, func(c *assert.CollectT) { diff --git a/server/channels/app/post_restore.go b/server/channels/app/post_restore.go index c93443a5bc..461d772c6c 100644 --- a/server/channels/app/post_restore.go +++ b/server/channels/app/post_restore.go @@ -13,7 +13,7 @@ import ( "github.com/mattermost/mattermost/server/public/shared/request" ) -func (a *App) RestorePostVersion(c request.CTX, userID, postID, restoreVersionID string) (*model.Post, *model.AppError) { +func (a *App) RestorePostVersion(c request.CTX, userID, postID, restoreVersionID string) (*model.Post, bool, *model.AppError) { toRestorePostVersion, err := a.Srv().Store().Post().GetSingle(c, restoreVersionID, true) if err != nil { var statusCode int @@ -25,24 +25,24 @@ func (a *App) RestorePostVersion(c request.CTX, userID, postID, restoreVersionID statusCode = http.StatusInternalServerError } - return nil, model.NewAppError("RestorePostVersion", "app.post.restore_post_version.get_single.app_error", nil, err.Error(), statusCode) + return nil, false, model.NewAppError("RestorePostVersion", "app.post.restore_post_version.get_single.app_error", nil, err.Error(), statusCode) } // restoreVersionID needs to be an old version of postID // this is only a safeguard and this should never happen in practice. if toRestorePostVersion.OriginalId != postID { - return nil, model.NewAppError("RestorePostVersion", "app.post.restore_post_version.not_an_history_item.app_error", nil, "", http.StatusBadRequest) + return nil, false, model.NewAppError("RestorePostVersion", "app.post.restore_post_version.not_an_history_item.app_error", nil, "", http.StatusBadRequest) } // the user needs to be the author of the post // this is only a safeguard and this should never happen in practice. if toRestorePostVersion.UserId != userID { - return nil, model.NewAppError("RestorePostVersion", "app.post.restore_post_version.not_allowed.app_error", nil, "", http.StatusForbidden) + return nil, false, model.NewAppError("RestorePostVersion", "app.post.restore_post_version.not_allowed.app_error", nil, "", http.StatusForbidden) } // the old version of post needs to be a deleted post if toRestorePostVersion.DeleteAt == 0 { - return nil, model.NewAppError("RestorePostVersion", "app.post.restore_post_version.not_valid_post_history_item.app_error", nil, "", http.StatusBadRequest) + return nil, false, model.NewAppError("RestorePostVersion", "app.post.restore_post_version.not_valid_post_history_item.app_error", nil, "", http.StatusBadRequest) } postPatch := &model.PostPatch{ diff --git a/server/channels/app/post_restore_test.go b/server/channels/app/post_restore_test.go index 70627efc8b..e74c9c0a2d 100644 --- a/server/channels/app/post_restore_test.go +++ b/server/channels/app/post_restore_test.go @@ -34,9 +34,10 @@ func TestRestorePostVersion(t *testing.T) { require.Equal(t, "new message 2", editHistory[0].Message) // now we'll restore a post version - restoredPost, appErr := th.App.RestorePostVersion(th.Context, th.BasicUser.Id, post.Id, editHistory[0].Id) + restoredPost, isMemberForPreview, appErr := th.App.RestorePostVersion(th.Context, th.BasicUser.Id, post.Id, editHistory[0].Id) require.Nil(t, appErr) require.Equal(t, "new message 2", restoredPost.Message) + require.True(t, isMemberForPreview) // verify from database fetchedPost, err = th.App.Srv().Store().Post().GetSingle(th.Context, post.Id, true) @@ -85,10 +86,11 @@ func TestRestorePostVersion(t *testing.T) { require.Equal(t, 1, len(editHistory[1].FileIds)) // now we'll restore a post version - restoredPost, appErr := th.App.RestorePostVersion(th.Context, th.BasicUser.Id, post.Id, editHistory[1].Id) + restoredPost, isMemberForPreview, appErr := th.App.RestorePostVersion(th.Context, th.BasicUser.Id, post.Id, editHistory[1].Id) require.Nil(t, appErr) require.Equal(t, "original message", restoredPost.Message) require.Equal(t, 1, len(restoredPost.FileIds)) + require.True(t, isMemberForPreview) // verify from database fetchedPost, err = th.App.Srv().Store().Post().GetSingle(th.Context, post.Id, true) @@ -125,7 +127,7 @@ func TestRestorePostVersion(t *testing.T) { // now we'll restore a post version otherPost := th.CreatePost(th.BasicChannel) - restoredPost, appErr := th.App.RestorePostVersion(th.Context, th.BasicUser.Id, post.Id, otherPost.Id) + restoredPost, _, appErr := th.App.RestorePostVersion(th.Context, th.BasicUser.Id, post.Id, otherPost.Id) require.NotNil(t, appErr) require.Equal(t, http.StatusBadRequest, appErr.StatusCode) require.Equal(t, "app.post.restore_post_version.not_an_history_item.app_error", appErr.Id) @@ -138,7 +140,7 @@ func TestRestorePostVersion(t *testing.T) { }) t.Run("should return an error if the post does not exist", func(t *testing.T) { - restoredPost, appErr := th.App.RestorePostVersion(th.Context, th.BasicUser.Id, model.NewId(), model.NewId()) + restoredPost, _, appErr := th.App.RestorePostVersion(th.Context, th.BasicUser.Id, model.NewId(), model.NewId()) require.NotNil(t, appErr) require.Equal(t, http.StatusNotFound, appErr.StatusCode) require.Equal(t, "app.post.restore_post_version.get_single.app_error", appErr.Id) @@ -150,7 +152,7 @@ func TestRestorePostVersion(t *testing.T) { // now we'll restore a post version invalidRestorePostIUd := model.NewId() - restoredPost, appErr := th.App.RestorePostVersion(th.Context, th.BasicUser.Id, post.Id, invalidRestorePostIUd) + restoredPost, _, appErr := th.App.RestorePostVersion(th.Context, th.BasicUser.Id, post.Id, invalidRestorePostIUd) require.NotNil(t, appErr) require.Equal(t, http.StatusNotFound, appErr.StatusCode) require.Equal(t, "app.post.restore_post_version.get_single.app_error", appErr.Id) @@ -172,7 +174,7 @@ func TestRestorePostVersion(t *testing.T) { require.Equal(t, "other post original message", otherPostEditHistory[0].Message) // we'll specify post's ID and other post's version ID, his should fail - restoredPost, appErr := th.App.RestorePostVersion(th.Context, th.BasicUser.Id, post.Id, otherPostEditHistory[0].Id) + restoredPost, _, appErr := th.App.RestorePostVersion(th.Context, th.BasicUser.Id, post.Id, otherPostEditHistory[0].Id) require.NotNil(t, appErr) require.Equal(t, "app.post.restore_post_version.not_an_history_item.app_error", appErr.Id) require.Equal(t, http.StatusBadRequest, appErr.StatusCode) diff --git a/server/channels/app/post_test.go b/server/channels/app/post_test.go index 4bac0e87ac..e074b72233 100644 --- a/server/channels/app/post_test.go +++ b/server/channels/app/post_test.go @@ -44,7 +44,7 @@ func TestCreatePostDeduplicate(t *testing.T) { pendingPostId := makePendingPostId(th.BasicUser) - post, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ + post, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "message", @@ -53,7 +53,7 @@ func TestCreatePostDeduplicate(t *testing.T) { require.Nil(t, err) require.Equal(t, "message", post.Message) - duplicatePost, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ + duplicatePost, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "message", @@ -100,7 +100,7 @@ func TestCreatePostDeduplicate(t *testing.T) { pendingPostId := makePendingPostId(th.BasicUser) - post, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ + post, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "message", @@ -110,7 +110,7 @@ func TestCreatePostDeduplicate(t *testing.T) { require.Equal(t, "Post rejected by plugin. rejected", err.Id) require.Nil(t, post) - duplicatePost, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ + duplicatePost, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "message", @@ -166,7 +166,7 @@ func TestCreatePostDeduplicate(t *testing.T) { go func() { defer wg.Done() var appErr *model.AppError - post, appErr = th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ + post, _, appErr = th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "plugin delayed", @@ -180,7 +180,7 @@ func TestCreatePostDeduplicate(t *testing.T) { time.Sleep(2 * time.Second) // Try creating a duplicate post - duplicatePost, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ + duplicatePost, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "plugin delayed", @@ -209,7 +209,7 @@ func TestCreatePostDeduplicate(t *testing.T) { pendingPostId := makePendingPostId(th.BasicUser) - post, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ + post, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "message", @@ -220,7 +220,7 @@ func TestCreatePostDeduplicate(t *testing.T) { time.Sleep(pendingPostIDsCacheTTL) - duplicatePost, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ + duplicatePost, _, err := th.App.CreatePostAsUser(th.Context.WithSession(session), &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "message", @@ -249,7 +249,7 @@ func TestCreatePostDeduplicate(t *testing.T) { privateChannel := th.CreatePrivateChannel(th.Context, th.BasicTeam) th.AddUserToChannel(th.BasicUser, privateChannel) - post, err := th.App.CreatePostAsUser(th.Context.WithSession(sessionBasicUser), &model.Post{ + post, _, err := th.App.CreatePostAsUser(th.Context.WithSession(sessionBasicUser), &model.Post{ UserId: th.BasicUser.Id, ChannelId: privateChannel.Id, Message: "message", @@ -258,7 +258,7 @@ func TestCreatePostDeduplicate(t *testing.T) { require.Nil(t, err) require.Equal(t, "message", post.Message) - postAsDifferentUser, err := th.App.CreatePostAsUser(th.Context.WithSession(sessionBasicUser2), &model.Post{ + postAsDifferentUser, _, err := th.App.CreatePostAsUser(th.Context.WithSession(sessionBasicUser2), &model.Post{ UserId: th.BasicUser2.Id, ChannelId: th.BasicChannel.Id, Message: "message2", @@ -357,18 +357,19 @@ func TestUpdatePostEditAt(t *testing.T) { post := th.BasicPost.Clone() post.IsPinned = true - saved, err := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) + saved, isMemberForPreviews, err := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) require.Nil(t, err) assert.Equal(t, saved.EditAt, post.EditAt, "shouldn't have updated post.EditAt when pinning post") + assert.True(t, isMemberForPreviews) post = saved.Clone() time.Sleep(time.Millisecond * 100) post.Message = model.NewId() - saved, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) + saved, isMemberForPreviews, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) require.Nil(t, err) assert.NotEqual(t, saved.EditAt, post.EditAt, "should have updated post.EditAt when updating post message") - + assert.True(t, isMemberForPreviews) time.Sleep(time.Millisecond * 200) } @@ -384,7 +385,7 @@ func TestUpdatePostTimeLimit(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.PostEditTimeLimit = -1 }) - _, err := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) + _, _, err := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) require.Nil(t, err) th.App.UpdateConfig(func(cfg *model.Config) { @@ -392,14 +393,14 @@ func TestUpdatePostTimeLimit(t *testing.T) { }) post.Message = model.NewId() - _, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) + _, _, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) require.Nil(t, err, "should allow you to edit the post") th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.PostEditTimeLimit = 1 }) post.Message = model.NewId() - _, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) + _, _, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) require.Nil(t, err, "should allow you to edit an old post because the time check is applied above in the call hierarchy") th.App.UpdateConfig(func(cfg *model.Config) { @@ -417,7 +418,7 @@ func TestUpdatePostInArchivedChannel(t *testing.T) { appErr := th.App.DeleteChannel(th.Context, archivedChannel, "") require.Nil(t, appErr) - _, err := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) + _, _, err := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) require.NotNil(t, err) require.Equal(t, "api.post.update_post.can_not_update_post_in_deleted.error", err.Id) } @@ -448,7 +449,7 @@ func TestPostReplyToPostWhereRootPosterLeftChannel(t *testing.T) { CreateAt: 0, } - _, err = th.App.CreatePostAsUser(th.Context, &replyPost, "", true) + _, _, err = th.App.CreatePostAsUser(th.Context, &replyPost, "", true) require.Nil(t, err) } @@ -470,7 +471,7 @@ func TestPostAttachPostToChildPost(t *testing.T) { CreateAt: 0, } - res1, err := th.App.CreatePostAsUser(th.Context, &replyPost1, "", true) + res1, _, err := th.App.CreatePostAsUser(th.Context, &replyPost1, "", true) require.Nil(t, err) replyPost2 := model.Post{ @@ -482,7 +483,7 @@ func TestPostAttachPostToChildPost(t *testing.T) { CreateAt: 0, } - _, err = th.App.CreatePostAsUser(th.Context, &replyPost2, "", true) + _, _, err = th.App.CreatePostAsUser(th.Context, &replyPost2, "", true) assert.Equalf(t, err.StatusCode, http.StatusBadRequest, "Expected BadRequest error, got %v", err) replyPost3 := model.Post{ @@ -494,7 +495,7 @@ func TestPostAttachPostToChildPost(t *testing.T) { CreateAt: 0, } - _, err = th.App.CreatePostAsUser(th.Context, &replyPost3, "", true) + _, _, err = th.App.CreatePostAsUser(th.Context, &replyPost3, "", true) assert.Nil(t, err) } @@ -557,7 +558,7 @@ func TestUpdatePostPluginHooks(t *testing.T) { }, true, th.App, th.Context) pendingPostId := makePendingPostId(th.BasicUser) - post, err := th.App.CreatePostAsUser(th.Context, &model.Post{ + post, _, err := th.App.CreatePostAsUser(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "message", @@ -566,7 +567,7 @@ func TestUpdatePostPluginHooks(t *testing.T) { require.Nil(t, err) post.Message = "new message" - updatedPost, err := th.App.UpdatePost(th.Context, post, nil) + updatedPost, _, err := th.App.UpdatePost(th.Context, post, nil) require.Nil(t, updatedPost) require.NotNil(t, err) require.Equal(t, "Post rejected by plugin. rejected", err.Id) @@ -624,7 +625,7 @@ func TestUpdatePostPluginHooks(t *testing.T) { }, true, th.App, th.Context) pendingPostId := makePendingPostId(th.BasicUser) - post, err := th.App.CreatePostAsUser(th.Context, &model.Post{ + post, _, err := th.App.CreatePostAsUser(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "message", @@ -633,7 +634,8 @@ func TestUpdatePostPluginHooks(t *testing.T) { require.Nil(t, err) post.Message = "new message" - updatedPost, err := th.App.UpdatePost(th.Context, post, nil) + updatedPost, isMemberForPreviews, err := th.App.UpdatePost(th.Context, post, nil) + require.True(t, isMemberForPreviews) require.Nil(t, err) require.NotNil(t, updatedPost) require.Equal(t, "2 new message 1", updatedPost.Message) @@ -682,7 +684,7 @@ func TestPostChannelMentions(t *testing.T) { CreateAt: 0, } - post, err = th.App.CreatePostAsUser(th.Context, post, "", true) + post, _, err = th.App.CreatePostAsUser(th.Context, post, "", true) require.Nil(t, err) assert.Equal(t, map[string]any{ "mention-test": map[string]any{ @@ -692,7 +694,8 @@ func TestPostChannelMentions(t *testing.T) { }, post.GetProp(model.PostPropsChannelMentions)) post.Message = fmt.Sprintf("goodbye, ~%v!", channelToMention2.Name) - result, err := th.App.UpdatePost(th.Context, post, nil) + result, isMemberForPreviews, err := th.App.UpdatePost(th.Context, post, nil) + require.True(t, isMemberForPreviews) require.Nil(t, err) assert.Equal(t, map[string]any{ "mention-test2": map[string]any{ @@ -702,7 +705,8 @@ func TestPostChannelMentions(t *testing.T) { }, result.GetProp(model.PostPropsChannelMentions)) result.Message = "no more mentions!" - result, err = th.App.UpdatePost(th.Context, result, nil) + result, isMemberForPreviews, err = th.App.UpdatePost(th.Context, result, nil) + require.True(t, isMemberForPreviews) require.Nil(t, err) assert.Nil(t, result.GetProp(model.PostPropsChannelMentions)) } @@ -861,7 +865,7 @@ func TestDeletePostWithFileAttachments(t *testing.T) { FileIds: []string{info1.Id}, } - post, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) assert.Nil(t, err) // Delete the post. @@ -917,7 +921,7 @@ func TestCreatePost(t *testing.T) { UserId: th.BasicUser.Id, } - rpost, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message) }) @@ -934,7 +938,7 @@ func TestCreatePost(t *testing.T) { Message: "This post does not have mentions", UserId: th.BasicUser.Id, } - rpost, err := th.App.CreatePost(th.Context, postWithNoMention, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, err := th.App.CreatePost(th.Context, postWithNoMention, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) assert.Equal(t, rpost.GetProps(), model.StringInterface{}) @@ -943,7 +947,7 @@ func TestCreatePost(t *testing.T) { Message: "This post has @here mention @all", UserId: th.BasicUser.Id, } - rpost, err = th.App.CreatePost(th.Context, postWithMention, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, err = th.App.CreatePost(th.Context, postWithMention, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) assert.Equal(t, rpost.GetProps(), model.StringInterface{}) }) @@ -957,7 +961,7 @@ func TestCreatePost(t *testing.T) { Message: "This post does not have mentions", UserId: th.BasicUser.Id, } - rpost, err := th.App.CreatePost(th.Context, postWithNoMention, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, err := th.App.CreatePost(th.Context, postWithNoMention, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) assert.Equal(t, rpost.GetProps(), model.StringInterface{}) @@ -966,7 +970,7 @@ func TestCreatePost(t *testing.T) { Message: "This post has @here mention @all", UserId: th.BasicUser.Id, } - rpost, err = th.App.CreatePost(th.Context, postWithMention, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, err = th.App.CreatePost(th.Context, postWithMention, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) assert.Equal(t, rpost.GetProp(model.PostPropsMentionHighlightDisabled), true) @@ -993,7 +997,7 @@ func TestCreatePost(t *testing.T) { th.Context.Session().UserId = th.BasicUser.Id - referencedPost, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{}) + referencedPost, _, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, err) permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id) @@ -1005,7 +1009,7 @@ func TestCreatePost(t *testing.T) { UserId: th.BasicUser.Id, } - previewPost, err = th.App.CreatePost(th.Context, previewPost, channelForPreview, model.CreatePostFlags{}) + previewPost, _, err = th.App.CreatePost(th.Context, previewPost, channelForPreview, model.CreatePostFlags{}) require.Nil(t, err) assert.Equal(t, previewPost.GetProps(), model.StringInterface{"previewed_post": referencedPost.Id}) @@ -1023,7 +1027,7 @@ func TestCreatePost(t *testing.T) { Message: "hello world", UserId: th.BasicUser.Id, } - referencedPost, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{}) + referencedPost, _, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, err) th.App.UpdateConfig(func(cfg *model.Config) { @@ -1039,7 +1043,7 @@ func TestCreatePost(t *testing.T) { UserId: th.BasicUser.Id, } - previewPost, err = th.App.CreatePost(th.Context, previewPost, channelForPreview, model.CreatePostFlags{}) + previewPost, _, err = th.App.CreatePost(th.Context, previewPost, channelForPreview, model.CreatePostFlags{}) require.Nil(t, err) sqlStore := th.GetSqlStore() @@ -1096,7 +1100,7 @@ func TestCreatePost(t *testing.T) { Message: "hello world", UserId: testCase.Author, } - referencedPost, err = th.App.CreatePost(th.Context, referencedPost, testCase.Channel, model.CreatePostFlags{}) + referencedPost, _, err = th.App.CreatePost(th.Context, referencedPost, testCase.Channel, model.CreatePostFlags{}) require.Nil(t, err) permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id) @@ -1106,7 +1110,7 @@ func TestCreatePost(t *testing.T) { UserId: th.BasicUser.Id, } - previewPost, err = th.App.CreatePost(th.Context, previewPost, th.BasicChannel, model.CreatePostFlags{}) + previewPost, _, err = th.App.CreatePost(th.Context, previewPost, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, err) require.Len(t, previewPost.Metadata.Embeds, testCase.Length) @@ -1149,7 +1153,7 @@ func TestCreatePost(t *testing.T) { Message: "hello world", UserId: user1.Id, } - createdPost, appErr := th.App.CreatePost(th.Context, newPost, dm, model.CreatePostFlags{}) + createdPost, _, appErr := th.App.CreatePost(th.Context, newPost, dm, model.CreatePostFlags{}) require.NotNil(t, appErr) require.Nil(t, createdPost) }) @@ -1190,7 +1194,7 @@ func TestCreatePost(t *testing.T) { Message: "hello world", UserId: user1.Id, } - createdPost, appErr := th.App.CreatePost(th.Context, newPost, gm, model.CreatePostFlags{}) + createdPost, _, appErr := th.App.CreatePost(th.Context, newPost, gm, model.CreatePostFlags{}) require.NotNil(t, appErr) require.Nil(t, createdPost) }) @@ -1213,7 +1217,7 @@ func TestCreatePost(t *testing.T) { Message: "hello world", UserId: th.BasicUser.Id, } - referencedPost, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{}) + referencedPost, _, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, err) th.App.UpdateConfig(func(cfg *model.Config) { @@ -1229,7 +1233,7 @@ func TestCreatePost(t *testing.T) { UserId: th.BasicUser.Id, } - previewPost, err = th.App.CreatePost(th.Context, previewPost, channelForPreview, model.CreatePostFlags{}) + previewPost, _, err = th.App.CreatePost(th.Context, previewPost, channelForPreview, model.CreatePostFlags{}) require.Nil(t, err) n := 1000 @@ -1239,7 +1243,7 @@ func TestCreatePost(t *testing.T) { go func() { defer wg.Done() post := previewPost.Clone() - _, appErr := th.App.UpdatePost(th.Context, post, nil) + _, _, appErr := th.App.UpdatePost(th.Context, post, nil) require.Nil(t, appErr) }() } @@ -1259,7 +1263,7 @@ func TestCreatePost(t *testing.T) { UserId: th.BasicUser.Id, } postToCreate.AddProp(model.PostPropsForceNotification, model.NewId()) - createdPost, err := th.App.CreatePost(th.Context, postToCreate, th.BasicChannel, model.CreatePostFlags{}) + createdPost, _, err := th.App.CreatePost(th.Context, postToCreate, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, err) require.Empty(t, createdPost.GetProp(model.PostPropsForceNotification)) }) @@ -1275,7 +1279,7 @@ func TestCreatePost(t *testing.T) { Message: "hello world", UserId: th.BasicUser.Id, } - createdPost, err := th.App.CreatePost(th.Context, postToCreate, th.BasicChannel, model.CreatePostFlags{ForceNotification: true}) + createdPost, _, err := th.App.CreatePost(th.Context, postToCreate, th.BasicChannel, model.CreatePostFlags{ForceNotification: true}) require.Nil(t, err) require.NotEmpty(t, createdPost.GetProp(model.PostPropsForceNotification)) }) @@ -1307,7 +1311,7 @@ func TestPatchPost(t *testing.T) { UserId: th.BasicUser.Id, } - rpost, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) assert.NotEqual(t, "![image]("+proxiedImageURL+")", rpost.Message) @@ -1315,7 +1319,7 @@ func TestPatchPost(t *testing.T) { Message: model.NewPointer("![image](" + imageURL + ")"), } - rpost, err = th.App.PatchPost(th.Context, rpost.Id, patch, nil) + rpost, _, err = th.App.PatchPost(th.Context, rpost.Id, patch, nil) require.Nil(t, err) assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message) }) @@ -1333,19 +1337,19 @@ func TestPatchPost(t *testing.T) { UserId: th.BasicUser.Id, } - rpost, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) t.Run("Does not set prop when user has USE_CHANNEL_MENTIONS", func(t *testing.T) { patchWithNoMention := &model.PostPatch{Message: model.NewPointer("This patch has no channel mention")} - rpost, err = th.App.PatchPost(th.Context, rpost.Id, patchWithNoMention, nil) + rpost, _, err = th.App.PatchPost(th.Context, rpost.Id, patchWithNoMention, nil) require.Nil(t, err) assert.Equal(t, rpost.GetProps(), model.StringInterface{}) patchWithMention := &model.PostPatch{Message: model.NewPointer("This patch has a mention now @here")} - rpost, err = th.App.PatchPost(th.Context, rpost.Id, patchWithMention, nil) + rpost, _, err = th.App.PatchPost(th.Context, rpost.Id, patchWithMention, nil) require.Nil(t, err) assert.Equal(t, rpost.GetProps(), model.StringInterface{}) }) @@ -1355,13 +1359,13 @@ func TestPatchPost(t *testing.T) { th.RemovePermissionFromRole(model.PermissionUseChannelMentions.Id, model.ChannelAdminRoleId) patchWithNoMention := &model.PostPatch{Message: model.NewPointer("This patch still does not have a mention")} - rpost, err = th.App.PatchPost(th.Context, rpost.Id, patchWithNoMention, nil) + rpost, _, err = th.App.PatchPost(th.Context, rpost.Id, patchWithNoMention, nil) require.Nil(t, err) assert.Equal(t, rpost.GetProps(), model.StringInterface{}) patchWithMention := &model.PostPatch{Message: model.NewPointer("This patch has a mention now @here")} - rpost, err = th.App.PatchPost(th.Context, rpost.Id, patchWithMention, nil) + rpost, _, err = th.App.PatchPost(th.Context, rpost.Id, patchWithMention, nil) require.Nil(t, err) assert.Equal(t, rpost.GetProp(model.PostPropsMentionHighlightDisabled), true) @@ -1388,7 +1392,7 @@ func TestCreatePostAsUser(t *testing.T) { require.NoError(t, err) time.Sleep(1 * time.Millisecond) - _, appErr := th.App.CreatePostAsUser(th.Context, post, "", true) + _, _, appErr := th.App.CreatePostAsUser(th.Context, post, "", true) require.Nil(t, appErr) channelMemberAfter, err := th.App.Srv().Store().Channel().GetMember(context.Background(), th.BasicChannel.Id, th.BasicUser.Id) @@ -1413,7 +1417,7 @@ func TestCreatePostAsUser(t *testing.T) { require.NoError(t, err) time.Sleep(1 * time.Millisecond) - _, appErr := th.App.CreatePostAsUser(th.Context, post, "", true) + _, _, appErr := th.App.CreatePostAsUser(th.Context, post, "", true) require.Nil(t, appErr) channelMemberAfter, err := th.App.Srv().Store().Channel().GetMember(context.Background(), th.BasicChannel.Id, th.BasicUser.Id) @@ -1445,7 +1449,7 @@ func TestCreatePostAsUser(t *testing.T) { require.NoError(t, err) time.Sleep(1 * time.Millisecond) - _, appErr = th.App.CreatePostAsUser(th.Context, post, "", true) + _, _, appErr = th.App.CreatePostAsUser(th.Context, post, "", true) require.Nil(t, appErr) channelMemberAfter, err := th.App.Srv().Store().Channel().GetMember(context.Background(), th.BasicChannel.Id, th.BasicUser.Id) @@ -1472,7 +1476,7 @@ func TestCreatePostAsUser(t *testing.T) { UserId: bot.UserId, } - _, appErr = th.App.CreatePostAsUser(th.Context, post, "", true) + _, _, appErr = th.App.CreatePostAsUser(th.Context, post, "", true) require.Nil(t, appErr) require.NoError(t, th.TestLogger.Flush()) @@ -1494,7 +1498,7 @@ func TestCreatePostAsUser(t *testing.T) { Message: "test", UserId: th.BasicUser2.Id, } - rootPost, appErr := th.App.CreatePostAsUser(th.Context, post, "", true) + rootPost, _, appErr := th.App.CreatePostAsUser(th.Context, post, "", true) require.Nil(t, appErr) channelMemberBefore, err := th.App.Srv().Store().Channel().GetMember(context.Background(), th.BasicChannel.Id, th.BasicUser.Id) @@ -1507,7 +1511,7 @@ func TestCreatePostAsUser(t *testing.T) { UserId: th.BasicUser.Id, RootId: rootPost.Id, } - _, appErr = th.App.CreatePostAsUser(th.Context, replyPost, "", true) + _, _, appErr = th.App.CreatePostAsUser(th.Context, replyPost, "", true) require.Nil(t, appErr) channelMemberAfter, err := th.App.Srv().Store().Channel().GetMember(context.Background(), th.BasicChannel.Id, th.BasicUser.Id) @@ -1531,7 +1535,7 @@ func TestCreatePostAsUser(t *testing.T) { Message: "test", UserId: th.BasicUser2.Id, } - rootPost, appErr := th.App.CreatePostAsUser(th.Context, post, "", true) + rootPost, _, appErr := th.App.CreatePostAsUser(th.Context, post, "", true) require.Nil(t, appErr) channelMemberBefore, err := th.App.Srv().Store().Channel().GetMember(context.Background(), th.BasicChannel.Id, th.BasicUser.Id) @@ -1544,7 +1548,7 @@ func TestCreatePostAsUser(t *testing.T) { UserId: th.BasicUser.Id, RootId: rootPost.Id, } - _, appErr = th.App.CreatePostAsUser(th.Context, replyPost, "", true) + _, _, appErr = th.App.CreatePostAsUser(th.Context, replyPost, "", true) require.Nil(t, appErr) channelMemberAfter, err := th.App.Srv().Store().Channel().GetMember(context.Background(), th.BasicChannel.Id, th.BasicUser.Id) @@ -1564,7 +1568,7 @@ func TestPatchPostInArchivedChannel(t *testing.T) { appErr := th.App.DeleteChannel(th.Context, archivedChannel, "") require.Nil(t, appErr) - _, err := th.App.PatchPost(th.Context, post.Id, &model.PostPatch{IsPinned: model.NewPointer(true)}, nil) + _, _, err := th.App.PatchPost(th.Context, post.Id, &model.PostPatch{IsPinned: model.NewPointer(true)}, nil) require.NotNil(t, err) require.Equal(t, "api.post.patch_post.can_not_update_post_in_deleted.error", err.Id) } @@ -1589,7 +1593,7 @@ func TestUpdateEphemeralPost(t *testing.T) { th.Context.Session().UserId = th.BasicUser.Id - referencedPost, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{}) + referencedPost, _, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, err) permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id) @@ -1600,7 +1604,7 @@ func TestUpdateEphemeralPost(t *testing.T) { UserId: th.BasicUser.Id, } - testPost = th.App.UpdateEphemeralPost(th.Context, th.BasicUser.Id, testPost) + testPost, _ = th.App.UpdateEphemeralPost(th.Context, th.BasicUser.Id, testPost) require.NotNil(t, testPost.Metadata) require.Len(t, testPost.Metadata.Embeds, 1) require.Equal(t, model.PostEmbedPermalink, testPost.Metadata.Embeds[0].Type) @@ -1626,7 +1630,7 @@ func TestUpdateEphemeralPost(t *testing.T) { th.Context.Session().UserId = th.BasicUser.Id - referencedPost, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{}) + referencedPost, _, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, err) permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id) @@ -1637,7 +1641,7 @@ func TestUpdateEphemeralPost(t *testing.T) { UserId: th.BasicUser2.Id, } - testPost = th.App.UpdateEphemeralPost(th.Context, th.BasicUser2.Id, testPost) + testPost, _ = th.App.UpdateEphemeralPost(th.Context, th.BasicUser2.Id, testPost) require.Nil(t, testPost.Metadata.Embeds) }) } @@ -1668,14 +1672,15 @@ func TestUpdatePost(t *testing.T) { UserId: th.BasicUser.Id, } - rpost, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) assert.NotEqual(t, "![image]("+proxiedImageURL+")", rpost.Message) post.Id = rpost.Id post.Message = "![image](" + imageURL + ")" - rpost, err = th.App.UpdatePost(th.Context, post, nil) + rpost, isMemberForPreviews, err := th.App.UpdatePost(th.Context, post, nil) + require.True(t, isMemberForPreviews) require.Nil(t, err) assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message) }) @@ -1698,7 +1703,7 @@ func TestUpdatePost(t *testing.T) { th.Context.Session().UserId = th.BasicUser.Id - referencedPost, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{}) + referencedPost, _, err := th.App.CreatePost(th.Context, referencedPost, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, err) permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id) @@ -1710,12 +1715,13 @@ func TestUpdatePost(t *testing.T) { UserId: th.BasicUser.Id, } - testPost, err = th.App.CreatePost(th.Context, testPost, channelForTestPost, model.CreatePostFlags{}) + testPost, _, err = th.App.CreatePost(th.Context, testPost, channelForTestPost, model.CreatePostFlags{}) require.Nil(t, err) assert.Equal(t, model.StringInterface{}, testPost.GetProps()) testPost.Message = permalink - testPost, err = th.App.UpdatePost(th.Context, testPost, nil) + testPost, isMemberForPreviews, err := th.App.UpdatePost(th.Context, testPost, nil) + require.True(t, isMemberForPreviews) require.Nil(t, err) assert.Equal(t, model.StringInterface{model.PostPropsPreviewedPost: referencedPost.Id}, testPost.GetProps()) }) @@ -1765,19 +1771,20 @@ func TestUpdatePost(t *testing.T) { Message: "hello world", UserId: testCase.Author, } - _, err = th.App.CreatePost(th.Context, referencedPost, testCase.Channel, model.CreatePostFlags{}) + _, _, err = th.App.CreatePost(th.Context, referencedPost, testCase.Channel, model.CreatePostFlags{}) require.Nil(t, err) previewPost := &model.Post{ ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id, } - previewPost, err = th.App.CreatePost(th.Context, previewPost, th.BasicChannel, model.CreatePostFlags{}) + previewPost, _, err = th.App.CreatePost(th.Context, previewPost, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, err) permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id) previewPost.Message = permalink - previewPost, err = th.App.UpdatePost(th.Context, previewPost, nil) + previewPost, isMemberForPreviews, err := th.App.UpdatePost(th.Context, previewPost, nil) + require.True(t, isMemberForPreviews) require.Nil(t, err) require.Len(t, previewPost.Metadata.Embeds, testCase.Length) @@ -1796,7 +1803,7 @@ func TestSearchPostsForUser(t *testing.T) { posts := make([]*model.Post, 7) for i := 0; i < cap(posts); i++ { - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: searchTerm, @@ -1830,7 +1837,7 @@ func TestSearchPostsForUser(t *testing.T) { page := 0 - results, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + results, allPostHaveMembership, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) assert.Nil(t, err) assert.Equal(t, []string{ @@ -1842,6 +1849,7 @@ func TestSearchPostsForUser(t *testing.T) { posts[1].Id, posts[0].Id, }, results.Order) + assert.True(t, allPostHaveMembership) }) t.Run("should not return later pages of posts from database", func(t *testing.T) { @@ -1851,10 +1859,11 @@ func TestSearchPostsForUser(t *testing.T) { page := 1 - results, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + results, allPostHaveMembership, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) assert.Nil(t, err) assert.Equal(t, []string{}, results.Order) + assert.True(t, allPostHaveMembership) }) t.Run("should return first page of posts from ElasticSearch", func(t *testing.T) { @@ -1881,10 +1890,11 @@ func TestSearchPostsForUser(t *testing.T) { th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil }() - results, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + results, allPostHaveMembership, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) assert.Nil(t, err) assert.Equal(t, resultsPage, results.Order) + assert.True(t, allPostHaveMembership) es.AssertExpectations(t) }) @@ -1909,10 +1919,11 @@ func TestSearchPostsForUser(t *testing.T) { th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil }() - results, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + results, allPostHaveMembership, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) assert.Nil(t, err) assert.Equal(t, resultsPage, results.Order) + assert.True(t, allPostHaveMembership) es.AssertExpectations(t) }) @@ -1934,7 +1945,7 @@ func TestSearchPostsForUser(t *testing.T) { th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil }() - results, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + results, allPostHaveMembership, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) assert.Nil(t, err) assert.Equal(t, []string{ @@ -1946,6 +1957,7 @@ func TestSearchPostsForUser(t *testing.T) { posts[1].Id, posts[0].Id, }, results.Order) + assert.True(t, allPostHaveMembership) es.AssertExpectations(t) }) @@ -1967,10 +1979,11 @@ func TestSearchPostsForUser(t *testing.T) { th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil }() - results, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + results, allPostHaveMembership, err := th.App.SearchPostsForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) assert.Nil(t, err) assert.Equal(t, []string{}, results.Order) + assert.True(t, allPostHaveMembership) es.AssertExpectations(t) }) @@ -1983,12 +1996,12 @@ func TestSearchPostsForUser(t *testing.T) { searchQueryWithPrefix := fmt.Sprintf("in:~%s %s", th.BasicChannel.Name, searchTerm) - resultsWithPrefix, err := th.App.SearchPostsForUser(th.Context, searchQueryWithPrefix, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + resultsWithPrefix, _, err := th.App.SearchPostsForUser(th.Context, searchQueryWithPrefix, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) assert.Nil(t, err) assert.Greater(t, len(resultsWithPrefix.PostList.Posts), 0, "searching using a tilde in front of a channel should return results") searchQueryWithoutPrefix := fmt.Sprintf("in:%s %s", th.BasicChannel.Name, searchTerm) - resultsWithoutPrefix, err := th.App.SearchPostsForUser(th.Context, searchQueryWithoutPrefix, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + resultsWithoutPrefix, _, err := th.App.SearchPostsForUser(th.Context, searchQueryWithoutPrefix, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) assert.Nil(t, err) assert.Equal(t, len(resultsWithPrefix.Posts), len(resultsWithoutPrefix.Posts), "searching using a tilde in front of a channel should return the same number of results") for k, v := range resultsWithPrefix.Posts { @@ -2005,12 +2018,12 @@ func TestSearchPostsForUser(t *testing.T) { searchQueryWithPrefix := fmt.Sprintf("from:@%s %s", th.BasicUser.Username, searchTerm) - resultsWithPrefix, err := th.App.SearchPostsForUser(th.Context, searchQueryWithPrefix, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + resultsWithPrefix, _, err := th.App.SearchPostsForUser(th.Context, searchQueryWithPrefix, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) assert.Nil(t, err) assert.Greater(t, len(resultsWithPrefix.PostList.Posts), 0, "searching using a 'at' symbol in front of a channel should return results") searchQueryWithoutPrefix := fmt.Sprintf("from:@%s %s", th.BasicUser.Username, searchTerm) - resultsWithoutPrefix, err := th.App.SearchPostsForUser(th.Context, searchQueryWithoutPrefix, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) + resultsWithoutPrefix, _, err := th.App.SearchPostsForUser(th.Context, searchQueryWithoutPrefix, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) assert.Nil(t, err) assert.Equal(t, len(resultsWithPrefix.Posts), len(resultsWithoutPrefix.Posts), "searching using an 'at' symbol in front of a channel should return the same number of results") for k, v := range resultsWithPrefix.Posts { @@ -2032,19 +2045,19 @@ func TestCountMentionsFromPost(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam) th.AddUserToChannel(user2, channel) - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test2", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test3", @@ -2070,19 +2083,19 @@ func TestCountMentionsFromPost(t *testing.T) { user2.NotifyProps[model.MentionKeysNotifyProp] = "apple" - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: fmt.Sprintf("@%s", user2.Username), }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test2", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "apple", @@ -2110,19 +2123,19 @@ func TestCountMentionsFromPost(t *testing.T) { user2.NotifyProps[model.ChannelMentionsNotifyProp] = "true" - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "@channel", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "@all", @@ -2150,19 +2163,19 @@ func TestCountMentionsFromPost(t *testing.T) { user2.NotifyProps[model.ChannelMentionsNotifyProp] = "false" - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "@channel", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "@all", @@ -2193,19 +2206,19 @@ func TestCountMentionsFromPost(t *testing.T) { }, channel.Id, user2.Id) require.Nil(t, err) - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "@channel", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "@all", @@ -2231,33 +2244,33 @@ func TestCountMentionsFromPost(t *testing.T) { user2.NotifyProps[model.CommentsNotifyProp] = model.CommentsNotifyRoot - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user2.Id, ChannelId: channel.Id, Message: "test", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, RootId: post1.Id, Message: "test2", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - post3, err := th.App.CreatePost(th.Context, &model.Post{ + post3, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test3", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user2.Id, ChannelId: channel.Id, RootId: post3.Id, Message: "test4", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, RootId: post3.Id, @@ -2286,33 +2299,33 @@ func TestCountMentionsFromPost(t *testing.T) { user2.NotifyProps[model.CommentsNotifyProp] = model.CommentsNotifyAny - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user2.Id, ChannelId: channel.Id, Message: "test", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, RootId: post1.Id, Message: "test2", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - post3, err := th.App.CreatePost(th.Context, &model.Post{ + post3, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test3", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user2.Id, ChannelId: channel.Id, RootId: post3.Id, Message: "test4", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, RootId: post3.Id, @@ -2339,7 +2352,7 @@ func TestCountMentionsFromPost(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam) th.AddUserToChannel(user2, channel) - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test", @@ -2349,7 +2362,7 @@ func TestCountMentionsFromPost(t *testing.T) { }, }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test2", @@ -2359,7 +2372,7 @@ func TestCountMentionsFromPost(t *testing.T) { }, }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test3", @@ -2389,14 +2402,14 @@ func TestCountMentionsFromPost(t *testing.T) { channel, err := th.App.createDirectChannel(th.Context, user1.Id, user2.Id) require.Nil(t, err) - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test2", @@ -2426,21 +2439,21 @@ func TestCountMentionsFromPost(t *testing.T) { channel, err := th.App.createGroupChannel(th.Context, []string{user1.Id, user2.Id, user3.Id}, user1.Id) require.Nil(t, err) - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test2", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user3.Id, ChannelId: channel.Id, Message: "test3", @@ -2469,19 +2482,19 @@ func TestCountMentionsFromPost(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam) th.AddUserToChannel(user2, channel) - _, err := th.App.CreatePost(th.Context, &model.Post{ + _, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: fmt.Sprintf("@%s", user2.Username), }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - post2, err := th.App.CreatePost(th.Context, &model.Post{ + post2, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test2", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: fmt.Sprintf("@%s", user2.Username), @@ -2507,13 +2520,13 @@ func TestCountMentionsFromPost(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam) th.AddUserToChannel(user2, channel) - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: fmt.Sprintf("@%s", user2.Username), }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user2.Id, ChannelId: channel.Id, Message: fmt.Sprintf("@%s", user2.Username), @@ -2541,26 +2554,26 @@ func TestCountMentionsFromPost(t *testing.T) { user2.NotifyProps[model.CommentsNotifyProp] = model.CommentsNotifyAny - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test1", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user2.Id, ChannelId: channel.Id, RootId: post1.Id, Message: "test2", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - post3, err := th.App.CreatePost(th.Context, &model.Post{ + post3, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test3", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, RootId: post1.Id, @@ -2591,13 +2604,13 @@ func TestCountMentionsFromPost(t *testing.T) { user2.NotifyProps[model.CommentsNotifyProp] = model.CommentsNotifyAny - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test1", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user2.Id, ChannelId: channel.Id, RootId: post1.Id, @@ -2607,13 +2620,13 @@ func TestCountMentionsFromPost(t *testing.T) { time.Sleep(time.Millisecond * 2) - post3, err := th.App.CreatePost(th.Context, &model.Post{ + post3, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test3", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, RootId: post1.Id, @@ -2648,19 +2661,19 @@ func TestCountMentionsFromPost(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam) th.AddUserToChannel(user2, channel) - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test1", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user2.Id, ChannelId: channel.Id, Message: fmt.Sprintf("@%s", user2.Username), }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user2.Id, ChannelId: channel.Id, Message: fmt.Sprintf("@%s", user2.Username), @@ -2691,7 +2704,7 @@ func TestCountMentionsFromPost(t *testing.T) { numPosts := 215 - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: fmt.Sprintf("@%s", user2.Username), @@ -2699,7 +2712,7 @@ func TestCountMentionsFromPost(t *testing.T) { require.Nil(t, err) for i := 0; i < numPosts-1; i++ { - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: fmt.Sprintf("@%s", user2.Username), @@ -2732,7 +2745,7 @@ func TestCountMentionsFromPost(t *testing.T) { user2.NotifyProps[model.MentionKeysNotifyProp] = "apple" - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: fmt.Sprintf("@%s", user2.Username), @@ -2744,14 +2757,14 @@ func TestCountMentionsFromPost(t *testing.T) { }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: fmt.Sprintf("@%s", user2.Username), }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "apple", @@ -2784,7 +2797,7 @@ func TestFillInPostProps(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam) - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "test123123 @group1 @group2 blah blah blah", @@ -2817,7 +2830,7 @@ func TestFillInPostProps(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam) th.AddUserToChannel(guest, channel) - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: guest.Id, ChannelId: channel.Id, Message: "test123123 @group1 @group2 blah blah blah", @@ -2851,7 +2864,7 @@ func TestFillInPostProps(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam) th.AddUserToChannel(guest, channel) - post1, err := th.App.CreatePost(th.Context, &model.Post{ + post1, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: guest.Id, ChannelId: channel.Id, Message: "test123123 @group1 @group2 blah blah blah", @@ -2886,7 +2899,7 @@ func TestFillInPostProps(t *testing.T) { dmChannelBetweenUser1AndUser2 := th.CreateDmChannel(user2) - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: dmChannelBetweenUser1AndUser2.Id, Message: "Testing out i should not be able to mention channel2 from team2? ~" + channel2.Name, @@ -2911,7 +2924,7 @@ func TestFillInPostProps(t *testing.T) { dmChannel := th.CreateDmChannel(user2) - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: dmChannel.Id, Message: "Check out ~" + channel.Name, @@ -2944,14 +2957,14 @@ func TestThreadMembership(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam) th.AddUserToChannel(user2, channel) - postRoot, err := th.App.CreatePost(th.Context, &model.Post{ + postRoot, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "root post", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, RootId: postRoot.Id, @@ -2968,14 +2981,14 @@ func TestThreadMembership(t *testing.T) { require.NoError(t, err2) require.Len(t, memberships, 1) - post2, err := th.App.CreatePost(th.Context, &model.Post{ + post2, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user2.Id, ChannelId: channel.Id, Message: "second post", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) - _, err = th.App.CreatePost(th.Context, &model.Post{ + _, _, err = th.App.CreatePost(th.Context, &model.Post{ UserId: user2.Id, ChannelId: channel.Id, RootId: post2.Id, @@ -3014,9 +3027,9 @@ func TestFollowThreadSkipsParticipants(t *testing.T) { appErr = th.App.JoinChannel(th.Context, channel, sysadmin.Id) require.Nil(t, appErr) - p1, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + sysadmin.Username}, channel, model.CreatePostFlags{}) + p1, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + sysadmin.Username}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) threadMembership, appErr := th.App.GetThreadMembershipForUser(user.Id, p1.Id) @@ -3025,7 +3038,7 @@ func TestFollowThreadSkipsParticipants(t *testing.T) { require.Nil(t, appErr) require.Len(t, thread.Participants, 1) // length should be 1, the original poster, since sysadmin was just mentioned but didn't post - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: sysadmin.Id, ChannelId: channel.Id, Message: "sysadmin reply"}, channel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: sysadmin.Id, ChannelId: channel.Id, Message: "sysadmin reply"}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) threadMembership, appErr = th.App.GetThreadMembershipForUser(user.Id, p1.Id) @@ -3077,12 +3090,12 @@ func TestAutofollowBasedOnRootPost(t *testing.T) { require.Nil(t, appErr) appErr = th.App.JoinChannel(th.Context, channel, user2.Id) require.Nil(t, appErr) - p1, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + user2.Username}, channel, model.CreatePostFlags{}) + p1, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + user2.Username}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) m, err := th.App.GetThreadMembershipsForUser(user2.Id, th.BasicTeam.Id) require.NoError(t, err) require.Len(t, m, 0) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) m, err = th.App.GetThreadMembershipsForUser(user2.Id, th.BasicTeam.Id) require.NoError(t, err) @@ -3106,9 +3119,9 @@ func TestViewChannelShouldNotUpdateThreads(t *testing.T) { require.Nil(t, appErr) appErr = th.App.JoinChannel(th.Context, channel, user2.Id) require.Nil(t, appErr) - p1, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + user2.Username}, channel, model.CreatePostFlags{}) + p1, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + user2.Username}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) m, err := th.App.GetThreadMembershipsForUser(user2.Id, th.BasicTeam.Id) require.NoError(t, err) @@ -3144,14 +3157,14 @@ func TestCollapsedThreadFetch(t *testing.T) { require.Nil(t, appErr) }() - postRoot, appErr := th.App.CreatePost(th.Context, &model.Post{ + postRoot, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "root post", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{ + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, RootId: postRoot.Id, @@ -3187,7 +3200,7 @@ func TestCollapsedThreadFetch(t *testing.T) { require.Nil(t, appErr) }() - postRoot, err := th.App.CreatePost(th.Context, &model.Post{ + postRoot, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "root post", @@ -3206,7 +3219,7 @@ func TestCollapsedThreadFetch(t *testing.T) { require.NotPanics(t, func() { // We're only testing that this doesn't panic, not checking the error // #nosec G104 - purposely not checking error as we're in a NotPanics block - _, _ = th.App.CreatePost(th.Context, &model.Post{ + _, _, _ = th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, RootId: postRoot.Id, @@ -3242,14 +3255,14 @@ func TestCollapsedThreadFetch(t *testing.T) { th.LinkUserToTeam(user3, th.BasicTeam) th.AddUserToChannel(user3, channel) - postRoot, appErr := th.App.CreatePost(th.Context, &model.Post{ + postRoot, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: user1.Id, ChannelId: channel.Id, Message: "root post", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{ + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{ UserId: user3.Id, ChannelId: channel.Id, RootId: postRoot.Id, @@ -3323,14 +3336,14 @@ func TestReplyToPostWithLag(t *testing.T) { mainHelper.ToggleReplicasOn() defer mainHelper.ToggleReplicasOff() - root, appErr := th.App.CreatePost(th.Context, &model.Post{ + root, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Message: "root post", }, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) - reply, appErr := th.App.CreatePost(th.Context, &model.Post{ + reply, _, appErr := th.App.CreatePost(th.Context, &model.Post{ UserId: th.BasicUser2.Id, ChannelId: th.BasicChannel.Id, RootId: root.Id, @@ -3356,7 +3369,7 @@ func TestSharedChannelSyncForPostActions(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam, WithShared(true)) - _, err := th.App.CreatePost(th.Context, &model.Post{ + _, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user.Id, ChannelId: channel.Id, Message: "Hello folks", @@ -3380,14 +3393,14 @@ func TestSharedChannelSyncForPostActions(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam, WithShared(true)) - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user.Id, ChannelId: channel.Id, Message: "Hello folks", }, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err, "Creating a post should not error") - _, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) + _, _, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true}) require.Nil(t, err, "Updating a post should not error") require.Len(t, sharedChannelService.channelNotifications, 2) @@ -3408,7 +3421,7 @@ func TestSharedChannelSyncForPostActions(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam, WithShared(true)) - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user.Id, ChannelId: channel.Id, Message: "Hello folks", @@ -3443,11 +3456,11 @@ func TestAutofollowOnPostingAfterUnfollow(t *testing.T) { require.Nil(t, appErr) appErr = th.App.JoinChannel(th.Context, channel, user2.Id) require.Nil(t, appErr) - p1, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + user2.Username}, channel, model.CreatePostFlags{}) + p1, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + user2.Username}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user2.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user2.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "reply"}, channel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "reply"}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) // unfollow thread @@ -3458,7 +3471,7 @@ func TestAutofollowOnPostingAfterUnfollow(t *testing.T) { require.NoError(t, err) require.False(t, m.Following) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "another reply"}, channel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "another reply"}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) // User should be following thread after posting in it, even after previously @@ -3475,7 +3488,7 @@ func TestGetPostIfAuthorized(t *testing.T) { t.Run("Private channel", func(t *testing.T) { privateChannel := th.CreatePrivateChannel(th.Context, th.BasicTeam) - post, err := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: privateChannel.Id, Message: "Hello"}, privateChannel, model.CreatePostFlags{}) + post, _, err := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: privateChannel.Id, Message: "Hello"}, privateChannel, model.CreatePostFlags{}) require.Nil(t, err) require.NotNil(t, post) @@ -3488,17 +3501,17 @@ func TestGetPostIfAuthorized(t *testing.T) { require.NotNil(t, session2) // User is not authorized to get post - _, err = th.App.GetPostIfAuthorized(th.Context, post.Id, session2, false) + _, err, _ = th.App.GetPostIfAuthorized(th.Context, post.Id, session2, false) require.NotNil(t, err) // User is authorized to get post - _, err = th.App.GetPostIfAuthorized(th.Context, post.Id, session1, false) + _, err, _ = th.App.GetPostIfAuthorized(th.Context, post.Id, session1, false) require.Nil(t, err) }) t.Run("Public channel", func(t *testing.T) { publicChannel := th.CreateChannel(th.Context, th.BasicTeam) - post, err := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: publicChannel.Id, Message: "Hello"}, publicChannel, model.CreatePostFlags{}) + post, _, err := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: publicChannel.Id, Message: "Hello"}, publicChannel, model.CreatePostFlags{}) require.Nil(t, err) require.NotNil(t, post) @@ -3511,11 +3524,11 @@ func TestGetPostIfAuthorized(t *testing.T) { require.NotNil(t, session2) // User is authorized to get post - _, err = th.App.GetPostIfAuthorized(th.Context, post.Id, session2, false) + _, err, _ = th.App.GetPostIfAuthorized(th.Context, post.Id, session2, false) require.Nil(t, err) // User is authorized to get post - _, err = th.App.GetPostIfAuthorized(th.Context, post.Id, session1, false) + _, err, _ = th.App.GetPostIfAuthorized(th.Context, post.Id, session1, false) require.Nil(t, err) th.App.UpdateConfig(func(c *model.Config) { @@ -3524,11 +3537,11 @@ func TestGetPostIfAuthorized(t *testing.T) { }) // User is not authorized to get post - _, err = th.App.GetPostIfAuthorized(th.Context, post.Id, session2, false) + _, err, _ = th.App.GetPostIfAuthorized(th.Context, post.Id, session2, false) require.NotNil(t, err) // User is authorized to get post - _, err = th.App.GetPostIfAuthorized(th.Context, post.Id, session1, false) + _, err, _ = th.App.GetPostIfAuthorized(th.Context, post.Id, session1, false) require.Nil(t, err) }) } @@ -3550,9 +3563,9 @@ func TestShouldNotRefollowOnOthersReply(t *testing.T) { require.Nil(t, appErr) appErr = th.App.JoinChannel(th.Context, channel, user2.Id) require.Nil(t, appErr) - p1, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + user2.Username}, channel, model.CreatePostFlags{}) + p1, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: user.Id, ChannelId: channel.Id, Message: "Hi @" + user2.Username}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user2.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user2.Id, ChannelId: channel.Id, Message: "Hola"}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) // User2 unfollows thread @@ -3564,7 +3577,7 @@ func TestShouldNotRefollowOnOthersReply(t *testing.T) { require.False(t, m.Following) // user posts in the thread - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "another reply"}, channel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "another reply"}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) // User2 should still not be following the thread because they manually @@ -3574,7 +3587,7 @@ func TestShouldNotRefollowOnOthersReply(t *testing.T) { require.False(t, m.Following) // user posts in the thread mentioning user2 - _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "reply with mention @" + user2.Username}, channel, model.CreatePostFlags{}) + _, _, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: p1.Id, UserId: user.Id, ChannelId: channel.Id, Message: "reply with mention @" + user2.Username}, channel, model.CreatePostFlags{}) require.Nil(t, appErr) // User2 should now be following the thread because they were explicitly mentioned @@ -3686,14 +3699,14 @@ func TestGetEditHistoryForPost(t *testing.T) { UserId: th.BasicUser.Id, } - rpost, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rpost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) // update the post message patch := &model.PostPatch{ Message: model.NewPointer("new message edited"), } - _, err1 := th.App.PatchPost(th.Context, rpost.Id, patch, nil) + _, _, err1 := th.App.PatchPost(th.Context, rpost.Id, patch, nil) require.Nil(t, err1) // update the post message again @@ -3701,7 +3714,7 @@ func TestGetEditHistoryForPost(t *testing.T) { Message: model.NewPointer("new message edited again"), } - _, err2 := th.App.PatchPost(th.Context, rpost.Id, patch, nil) + _, _, err2 := th.App.PatchPost(th.Context, rpost.Id, patch, nil) require.Nil(t, err2) t.Run("should return the edit history", func(t *testing.T) { @@ -3731,25 +3744,25 @@ func TestGetEditHistoryForPost(t *testing.T) { FileIds: model.StringArray{fileInfo.Id}, } - _, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + _, _, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) patch := &model.PostPatch{ Message: model.NewPointer("new message edited"), } - _, appErr := th.App.PatchPost(th.Context, post.Id, patch, nil) + _, _, appErr := th.App.PatchPost(th.Context, post.Id, patch, nil) require.Nil(t, appErr) patch = &model.PostPatch{ Message: model.NewPointer("new message edited 2"), } - _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil) + _, _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil) require.Nil(t, appErr) patch = &model.PostPatch{ Message: model.NewPointer("new message edited 3"), } - _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil) + _, _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil) require.Nil(t, appErr) edits, err := th.App.GetEditHistoryForPost(post.Id) @@ -3777,25 +3790,25 @@ func TestGetEditHistoryForPost(t *testing.T) { FileIds: model.StringArray{fileInfo.Id}, } - _, appErr = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + _, _, appErr = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, appErr) patch := &model.PostPatch{ Message: model.NewPointer("new message edited"), } - _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil) + _, _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil) require.Nil(t, appErr) patch = &model.PostPatch{ Message: model.NewPointer("new message edited 2"), } - _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil) + _, _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil) require.Nil(t, appErr) patch = &model.PostPatch{ Message: model.NewPointer("new message edited 3"), } - _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil) + _, _, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil) require.Nil(t, appErr) // now delete the file info, and it should still be include in edit history metadata @@ -3832,7 +3845,7 @@ func TestCopyWranglerPostlist(t *testing.T) { UserId: th.BasicUser.Id, FileIds: []string{fileInfo.Id}, } - rootPost, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + rootPost, _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) // Add a reaction to the post @@ -3856,7 +3869,7 @@ func TestCopyWranglerPostlist(t *testing.T) { Posts: []*model.Post{rootPost}, FileAttachmentCount: 1, } - newRootPost, err := th.App.CopyWranglerPostlist(th.Context, wpl, targetChannel) + newRootPost, _, err := th.App.CopyWranglerPostlist(th.Context, wpl, targetChannel) require.Nil(t, err) // Check that the new post has the same message and file attachment @@ -4020,7 +4033,7 @@ func TestPermanentDeletePost(t *testing.T) { FileIds: []string{info1.Id}, } - post, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) assert.Nil(t, err) // Delete the post. @@ -4059,7 +4072,7 @@ func TestPermanentDeletePost(t *testing.T) { FileIds: []string{info1.Id}, } - post, appErr = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + post, _, appErr = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) assert.Nil(t, appErr) infos, err := th.App.Srv().Store().FileInfo().GetForPost(post.Id, true, true, false) @@ -4247,7 +4260,7 @@ func TestFilterPostsByChannelPermissions(t *testing.T) { postList.Posts[post3.Id] = post3 postList.Order = []string{post1.Id, post2.Id, post3.Id} - appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, th.BasicUser.Id) + _, appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, th.BasicUser.Id) require.Nil(t, appErr) require.Len(t, postList.Posts, 3) require.Len(t, postList.Order, 3) @@ -4260,7 +4273,7 @@ func TestFilterPostsByChannelPermissions(t *testing.T) { postList.Posts[post3.Id] = post3 postList.Order = []string{post1.Id, post2.Id, post3.Id} - appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, guestUser.Id) + _, appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, guestUser.Id) require.Nil(t, appErr) require.Len(t, postList.Posts, 3) require.Len(t, postList.Order, 3) @@ -4298,7 +4311,7 @@ func TestFilterPostsByChannelPermissions(t *testing.T) { postList.Posts[post3.Id] = post3 postList.Order = []string{post1.Id, post2.Id, post3.Id} - appErr = th.App.FilterPostsByChannelPermissions(th.Context, postList, guestUser.Id) + _, appErr = th.App.FilterPostsByChannelPermissions(th.Context, postList, guestUser.Id) require.Nil(t, appErr) require.Len(t, postList.Posts, 0) require.Len(t, postList.Order, 0) @@ -4306,14 +4319,14 @@ func TestFilterPostsByChannelPermissions(t *testing.T) { t.Run("should handle empty post list", func(t *testing.T) { postList := model.NewPostList() - appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, th.BasicUser.Id) + _, appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, th.BasicUser.Id) require.Nil(t, appErr) require.Len(t, postList.Posts, 0) require.Len(t, postList.Order, 0) }) t.Run("should handle nil post list", func(t *testing.T) { - appErr := th.App.FilterPostsByChannelPermissions(th.Context, nil, th.BasicUser.Id) + _, appErr := th.App.FilterPostsByChannelPermissions(th.Context, nil, th.BasicUser.Id) require.Nil(t, appErr) }) @@ -4327,7 +4340,7 @@ func TestFilterPostsByChannelPermissions(t *testing.T) { postList.Posts[postWithoutChannel.Id] = postWithoutChannel postList.Order = []string{postWithoutChannel.Id} - appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, th.BasicUser.Id) + _, appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, th.BasicUser.Id) require.Nil(t, appErr) require.Len(t, postList.Posts, 0) require.Len(t, postList.Order, 0) @@ -4343,7 +4356,7 @@ func TestFilterPostsByChannelPermissions(t *testing.T) { postList.Posts[postWithInvalidChannel.Id] = postWithInvalidChannel postList.Order = []string{postWithInvalidChannel.Id} - appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, th.BasicUser.Id) + _, appErr := th.App.FilterPostsByChannelPermissions(th.Context, postList, th.BasicUser.Id) require.Nil(t, appErr) require.Len(t, postList.Posts, 0) require.Len(t, postList.Order, 0) diff --git a/server/channels/app/reaction_test.go b/server/channels/app/reaction_test.go index b50bfeec30..060c682d1e 100644 --- a/server/channels/app/reaction_test.go +++ b/server/channels/app/reaction_test.go @@ -111,7 +111,7 @@ func TestSharedChannelSyncForReactionActions(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam, WithShared(true)) - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user.Id, ChannelId: channel.Id, Message: "Hello folks", @@ -146,7 +146,7 @@ func TestSharedChannelSyncForReactionActions(t *testing.T) { channel := th.CreateChannel(th.Context, th.BasicTeam, WithShared(true)) - post, err := th.App.CreatePost(th.Context, &model.Post{ + post, _, err := th.App.CreatePost(th.Context, &model.Post{ UserId: user.Id, ChannelId: channel.Id, Message: "Hello folks", diff --git a/server/channels/app/report.go b/server/channels/app/report.go index b2fc17e5f0..8c8b505cdd 100644 --- a/server/channels/app/report.go +++ b/server/channels/app/report.go @@ -136,7 +136,7 @@ func (a *App) SendReportToUser(rctx request.CTX, job *model.Job, format string) FileIds: []string{fileInfo.Id}, } - _, err = a.CreatePost(rctx, post, channel, model.CreatePostFlags{SetOnline: true}) + _, _, err = a.CreatePost(rctx, post, channel, model.CreatePostFlags{SetOnline: true}) return err } @@ -250,7 +250,7 @@ func (a *App) StartUsersBatchExport(rctx request.CTX, ro *model.UserReportOption UserId: systemBot.UserId, } - if _, err := a.CreatePost(rctx, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(rctx, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { rctx.Logger().Error("Failed to post batch export message", mlog.Err(err)) } }) diff --git a/server/channels/app/scheduled_post_job.go b/server/channels/app/scheduled_post_job.go index ede685811c..6f75d7307c 100644 --- a/server/channels/app/scheduled_post_job.go +++ b/server/channels/app/scheduled_post_job.go @@ -195,7 +195,7 @@ func (a *App) postScheduledPost(rctx request.CTX, scheduledPost *model.Scheduled TriggerWebhooks: true, SetOnline: false, } - _, appErr = a.CreatePost(rctx, post, channel, createPostFlags) + _, _, appErr = a.CreatePost(rctx, post, channel, createPostFlags) if appErr != nil { rctx.Logger().Error( "App.processScheduledPostBatch: failed to post scheduled post", @@ -454,7 +454,7 @@ func (a *App) notifyUser(rctx request.CTX, userId string, userFailedMessages []* UserId: systemBot.UserId, } - if _, err := a.CreatePost(rctx, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(rctx, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { rctx.Logger().Error("Failed to post notification about failed scheduled messages", mlog.Err(err)) } } diff --git a/server/channels/app/shared_channel_test.go b/server/channels/app/shared_channel_test.go index ad4e506030..a501543962 100644 --- a/server/channels/app/shared_channel_test.go +++ b/server/channels/app/shared_channel_test.go @@ -366,7 +366,7 @@ func TestApp_RemoteUnsharing(t *testing.T) { UserId: th.BasicUser.Id, Message: "Test message after remote 1 unshare", } - _, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{}) + _, _, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{}) require.Nil(t, appErr) // Get post count after creating the test post but before "remote-initiated unshare" diff --git a/server/channels/app/slashcommands/auto_posts.go b/server/channels/app/slashcommands/auto_posts.go index 9c66c84907..3e7680bb89 100644 --- a/server/channels/app/slashcommands/auto_posts.go +++ b/server/channels/app/slashcommands/auto_posts.go @@ -114,7 +114,7 @@ func (cfg *AutoPostCreator) CreateRandomPostNested(c request.CTX, rootID string) post.UserId = cfg.UsersToPostFrom[i] } } - rpost, err := cfg.a.CreatePostMissingChannel(c, post, true, true) + rpost, _, err := cfg.a.CreatePostMissingChannel(c, post, true, true) if err != nil { return nil, err } diff --git a/server/channels/app/slashcommands/command_channel_header.go b/server/channels/app/slashcommands/command_channel_header.go index 0403e14274..0883730b45 100644 --- a/server/channels/app/slashcommands/command_channel_header.go +++ b/server/channels/app/slashcommands/command_channel_header.go @@ -46,7 +46,7 @@ func (*HeaderProvider) DoCommand(a *app.App, c request.CTX, args *model.CommandA switch channel.Type { case model.ChannelTypeOpen: - if !a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePublicChannelProperties) { + if ok, _ := a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePublicChannelProperties); !ok { return &model.CommandResponse{ Text: args.T("api.command_channel_header.permission.app_error"), ResponseType: model.CommandResponseTypeEphemeral, @@ -54,7 +54,7 @@ func (*HeaderProvider) DoCommand(a *app.App, c request.CTX, args *model.CommandA } case model.ChannelTypePrivate: - if !a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePrivateChannelProperties) { + if ok, _ := a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePrivateChannelProperties); !ok { return &model.CommandResponse{ Text: args.T("api.command_channel_header.permission.app_error"), ResponseType: model.CommandResponseTypeEphemeral, diff --git a/server/channels/app/slashcommands/command_channel_purpose.go b/server/channels/app/slashcommands/command_channel_purpose.go index 62fc4e8417..0b2036e87d 100644 --- a/server/channels/app/slashcommands/command_channel_purpose.go +++ b/server/channels/app/slashcommands/command_channel_purpose.go @@ -46,14 +46,14 @@ func (*PurposeProvider) DoCommand(a *app.App, c request.CTX, args *model.Command switch channel.Type { case model.ChannelTypeOpen: - if !a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePublicChannelProperties) { + if ok, _ := a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePublicChannelProperties); !ok { return &model.CommandResponse{ Text: args.T("api.command_channel_purpose.permission.app_error"), ResponseType: model.CommandResponseTypeEphemeral, } } case model.ChannelTypePrivate: - if !a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePrivateChannelProperties) { + if ok, _ := a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePrivateChannelProperties); !ok { return &model.CommandResponse{ Text: args.T("api.command_channel_purpose.permission.app_error"), ResponseType: model.CommandResponseTypeEphemeral, diff --git a/server/channels/app/slashcommands/command_channel_rename.go b/server/channels/app/slashcommands/command_channel_rename.go index c387ee2265..1062635584 100644 --- a/server/channels/app/slashcommands/command_channel_rename.go +++ b/server/channels/app/slashcommands/command_channel_rename.go @@ -49,14 +49,14 @@ func (*RenameProvider) DoCommand(a *app.App, c request.CTX, args *model.CommandA switch channel.Type { case model.ChannelTypeOpen: - if !a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePublicChannelProperties) { + if ok, _ := a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePublicChannelProperties); !ok { return &model.CommandResponse{ Text: args.T("api.command_channel_rename.permission.app_error"), ResponseType: model.CommandResponseTypeEphemeral, } } case model.ChannelTypePrivate: - if !a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePrivateChannelProperties) { + if ok, _ := a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePrivateChannelProperties); !ok { return &model.CommandResponse{ Text: args.T("api.command_channel_rename.permission.app_error"), ResponseType: model.CommandResponseTypeEphemeral, diff --git a/server/channels/app/slashcommands/command_echo.go b/server/channels/app/slashcommands/command_echo.go index 989ffc645e..b32b725ee3 100644 --- a/server/channels/app/slashcommands/command_echo.go +++ b/server/channels/app/slashcommands/command_echo.go @@ -89,7 +89,7 @@ func (*EchoProvider) DoCommand(a *app.App, c request.CTX, args *model.CommandArg time.Sleep(time.Duration(delay) * time.Second) - if _, err := a.CreatePostMissingChannel(c, post, true, true); err != nil { + if _, _, err := a.CreatePostMissingChannel(c, post, true, true); err != nil { c.Logger().Error("Unable to create /echo post.", mlog.Err(err)) } }) diff --git a/server/channels/app/slashcommands/command_groupmsg.go b/server/channels/app/slashcommands/command_groupmsg.go index 11daf7011a..048102dd55 100644 --- a/server/channels/app/slashcommands/command_groupmsg.go +++ b/server/channels/app/slashcommands/command_groupmsg.go @@ -126,7 +126,7 @@ func (*groupmsgProvider) DoCommand(a *app.App, c request.CTX, args *model.Comman post.Message = parsedMessage post.ChannelId = groupChannel.Id post.UserId = args.UserId - if _, err := a.CreatePostMissingChannel(c, post, true, true); err != nil { + if _, _, err := a.CreatePostMissingChannel(c, post, true, true); err != nil { return &model.CommandResponse{Text: args.T("api.command_groupmsg.fail.app_error"), ResponseType: model.CommandResponseTypeEphemeral} } } diff --git a/server/channels/app/slashcommands/command_invite.go b/server/channels/app/slashcommands/command_invite.go index 4400ea565a..2547480086 100644 --- a/server/channels/app/slashcommands/command_invite.go +++ b/server/channels/app/slashcommands/command_invite.go @@ -274,7 +274,7 @@ func (i *InviteProvider) checkPermissions(a *app.App, c request.CTX, args *model for _, targetChannel := range targetChannels { switch targetChannel.Type { case model.ChannelTypeOpen: - if !a.HasPermissionToChannel(c, args.UserId, targetChannel.Id, model.PermissionManagePublicChannelMembers) { + if ok, _ := a.HasPermissionToChannel(c, args.UserId, targetChannel.Id, model.PermissionManagePublicChannelMembers); !ok { *resps = append(*resps, args.T("api.command_invite.permission.app_error", map[string]any{ "User": targetUser.Username, "Channel": targetChannel.Name, @@ -282,7 +282,7 @@ func (i *InviteProvider) checkPermissions(a *app.App, c request.CTX, args *model continue } case model.ChannelTypePrivate: - if !a.HasPermissionToChannel(c, args.UserId, targetChannel.Id, model.PermissionManagePrivateChannelMembers) { + if ok, _ := a.HasPermissionToChannel(c, args.UserId, targetChannel.Id, model.PermissionManagePrivateChannelMembers); !ok { if _, err = a.GetChannelMember(c, targetChannel.Id, args.UserId); err == nil { // User doing the inviting is a member of the channel. *resps = append(*resps, args.T("api.command_invite.permission.app_error", map[string]any{ diff --git a/server/channels/app/slashcommands/command_join.go b/server/channels/app/slashcommands/command_join.go index 2bc6be9066..fdae10ae15 100644 --- a/server/channels/app/slashcommands/command_join.go +++ b/server/channels/app/slashcommands/command_join.go @@ -55,11 +55,11 @@ func (*JoinProvider) DoCommand(a *app.App, c request.CTX, args *model.CommandArg switch channel.Type { case model.ChannelTypeOpen: - if !a.HasPermissionToChannel(c, args.UserId, channel.Id, model.PermissionJoinPublicChannels) { + if ok, _ := a.HasPermissionToChannel(c, args.UserId, channel.Id, model.PermissionJoinPublicChannels); !ok { return &model.CommandResponse{Text: args.T("api.command_join.fail.app_error"), ResponseType: model.CommandResponseTypeEphemeral} } case model.ChannelTypePrivate: - if !a.HasPermissionToChannel(c, args.UserId, channel.Id, model.PermissionReadChannel) { + if ok, _ := a.HasPermissionToChannel(c, args.UserId, channel.Id, model.PermissionReadChannel); !ok { return &model.CommandResponse{Text: args.T("api.command_join.fail.app_error"), ResponseType: model.CommandResponseTypeEphemeral} } default: diff --git a/server/channels/app/slashcommands/command_loadtest.go b/server/channels/app/slashcommands/command_loadtest.go index 402261cec4..b96af05d33 100644 --- a/server/channels/app/slashcommands/command_loadtest.go +++ b/server/channels/app/slashcommands/command_loadtest.go @@ -672,7 +672,7 @@ func (*LoadTestProvider) URLCommand(a *app.App, c request.CTX, args *model.Comma post.ChannelId = args.ChannelId post.UserId = args.UserId - if _, err := a.CreatePostMissingChannel(c, post, false, true); err != nil { + if _, _, err := a.CreatePostMissingChannel(c, post, false, true); err != nil { return &model.CommandResponse{Text: "Unable to create post", ResponseType: model.CommandResponseTypeEphemeral}, err } } @@ -721,7 +721,7 @@ func (*LoadTestProvider) JSONCommand(a *app.App, c request.CTX, args *model.Comm post.Message = message } - if _, err := a.CreatePostMissingChannel(c, &post, false, true); err != nil { + if _, _, err := a.CreatePostMissingChannel(c, &post, false, true); err != nil { return &model.CommandResponse{Text: "Unable to create post", ResponseType: model.CommandResponseTypeEphemeral}, err } diff --git a/server/channels/app/slashcommands/command_msg.go b/server/channels/app/slashcommands/command_msg.go index fc45a8fbb8..33def5a202 100644 --- a/server/channels/app/slashcommands/command_msg.go +++ b/server/channels/app/slashcommands/command_msg.go @@ -100,7 +100,7 @@ func (*msgProvider) DoCommand(a *app.App, c request.CTX, args *model.CommandArgs post.Message = parsedMessage post.ChannelId = targetChannelID post.UserId = args.UserId - if _, err = a.CreatePostMissingChannel(c, post, true, true); err != nil { + if _, _, err = a.CreatePostMissingChannel(c, post, true, true); err != nil { return &model.CommandResponse{Text: args.T("api.command_msg.fail.app_error"), ResponseType: model.CommandResponseTypeEphemeral} } } diff --git a/server/channels/app/slashcommands/command_remove.go b/server/channels/app/slashcommands/command_remove.go index 3ab1b23e7b..c0f5b0f75a 100644 --- a/server/channels/app/slashcommands/command_remove.go +++ b/server/channels/app/slashcommands/command_remove.go @@ -75,14 +75,14 @@ func doCommand(a *app.App, c request.CTX, args *model.CommandArgs, message strin switch channel.Type { case model.ChannelTypeOpen: - if !a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePublicChannelMembers) { + if ok, _ := a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePublicChannelMembers); !ok { return &model.CommandResponse{ Text: args.T("api.command_remove.permission.app_error"), ResponseType: model.CommandResponseTypeEphemeral, } } case model.ChannelTypePrivate: - if !a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePrivateChannelMembers) { + if ok, _ := a.HasPermissionToChannel(c, args.UserId, args.ChannelId, model.PermissionManagePrivateChannelMembers); !ok { return &model.CommandResponse{ Text: args.T("api.command_remove.permission.app_error"), ResponseType: model.CommandResponseTypeEphemeral, diff --git a/server/channels/app/slashcommands/helper_test.go b/server/channels/app/slashcommands/helper_test.go index 503b839b52..578253a0e7 100644 --- a/server/channels/app/slashcommands/helper_test.go +++ b/server/channels/app/slashcommands/helper_test.go @@ -344,7 +344,7 @@ func (th *TestHelper) createPost(tb testing.TB, channel *model.Channel) *model.P CreateAt: model.GetMillis() - 10000, } - post, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{SetOnline: true}) + post, _, appErr := th.App.CreatePost(th.Context, post, channel, model.CreatePostFlags{SetOnline: true}) require.Nil(tb, appErr) return post } diff --git a/server/channels/app/team.go b/server/channels/app/team.go index 6f41019a63..b1060e3ea9 100644 --- a/server/channels/app/team.go +++ b/server/channels/app/team.go @@ -1288,7 +1288,7 @@ func (a *App) postLeaveTeamMessage(c request.CTX, user *model.User, channel *mod }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("postRemoveFromChannelMessage", "api.channel.post_user_add_remove_message_and_forget.error", nil, "", http.StatusInternalServerError).Wrap(err) } @@ -1306,7 +1306,7 @@ func (a *App) postRemoveFromTeamMessage(c request.CTX, user *model.User, channel }, } - if _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { + if _, _, err := a.CreatePost(c, post, channel, model.CreatePostFlags{SetOnline: true}); err != nil { return model.NewAppError("postRemoveFromTeamMessage", "api.channel.post_user_add_remove_message_and_forget.error", nil, "", http.StatusInternalServerError).Wrap(err) } diff --git a/server/channels/app/team_test.go b/server/channels/app/team_test.go index 47f8ee5594..e47d691214 100644 --- a/server/channels/app/team_test.go +++ b/server/channels/app/team_test.go @@ -1187,12 +1187,14 @@ func TestAppUpdateTeamScheme(t *testing.T) { }, } // ensure user can update channel properties before applying the scheme - require.True(t, th.App.SessionHasPermissionToChannel(th.Context, session, channel.Id, model.PermissionManagePublicChannelProperties)) + ok, _ := th.App.SessionHasPermissionToChannel(th.Context, session, channel.Id, model.PermissionManagePublicChannelProperties) + require.True(t, ok) // apply the team scheme team2.SchemeId = &team2Scheme.Id _, appErr = th.App.UpdateTeamScheme(team2) require.Nil(t, appErr) - require.False(t, th.App.SessionHasPermissionToChannel(th.Context, session, channel.Id, model.PermissionManagePublicChannelProperties)) + ok, _ = th.App.SessionHasPermissionToChannel(th.Context, session, channel.Id, model.PermissionManagePublicChannelProperties) + require.False(t, ok) } func TestGetTeamMembers(t *testing.T) { diff --git a/server/channels/app/user.go b/server/channels/app/user.go index c00e1084a7..acb1ede8e3 100644 --- a/server/channels/app/user.go +++ b/server/channels/app/user.go @@ -2895,7 +2895,7 @@ func (a *App) UpdateThreadFollowForUserFromChannelAdd(c request.CTX, userID, tea } a.sanitizeProfiles(userThread.Participants, false) userThread.Post.SanitizeProps() - sanitizedPost, appErr := a.SanitizePostMetadataForUser(c, userThread.Post, userID) + sanitizedPost, isMemberForPreviews, appErr := a.SanitizePostMetadataForUser(c, userThread.Post, userID) if appErr != nil { return appErr } @@ -2909,6 +2909,16 @@ func (a *App) UpdateThreadFollowForUserFromChannelAdd(c request.CTX, userID, tea message.Add("previous_unread_replies", int64(0)) message.Add("previous_unread_mentions", int64(0)) + auditRec := a.MakeAuditRecord(c, "websocketPost", model.AuditStatusSuccess) + defer a.LogAuditRec(c, auditRec, nil) + model.AddEventParameterToAuditRec(auditRec, "post_id", userThread.Post.Id) + model.AddEventParameterToAuditRec(auditRec, "user_id", userID) + model.AddEventParameterToAuditRec(auditRec, "source", "UpdateThreadFollowForUserFromChannelAdd") + if !isMemberForPreviews { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + auditRec.Success() + a.Publish(message) return nil } diff --git a/server/channels/app/user_test.go b/server/channels/app/user_test.go index 6e2c7e1723..657f2fb870 100644 --- a/server/channels/app/user_test.go +++ b/server/channels/app/user_test.go @@ -2180,9 +2180,9 @@ func TestUpdateThreadReadForUser(t *testing.T) { *cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn }) - rootPost, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, model.CreatePostFlags{}) + rootPost, _, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) - replyPost, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, model.CreatePostFlags{}) + replyPost, _, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, model.CreatePostFlags{}) require.Nil(t, appErr) threads, appErr := th.App.GetThreadsForUser(th.BasicUser.Id, th.BasicTeam.Id, model.GetUserThreadsOpts{}) require.Nil(t, appErr) diff --git a/server/channels/app/web_broadcast_hooks.go b/server/channels/app/web_broadcast_hooks.go index 512c030774..1c3f8ffe8a 100644 --- a/server/channels/app/web_broadcast_hooks.go +++ b/server/channels/app/web_broadcast_hooks.go @@ -151,7 +151,8 @@ func (h *permalinkBroadcastHook) Process(msg *platform.HookedWebSocketEvent, web } rctx := request.EmptyContext(webConn.Platform.Log()) - if !webConn.Suite.HasPermissionToReadChannel(rctx, webConn.UserId, previewChannel) { + ok, isMember := webConn.Suite.HasPermissionToReadChannel(rctx, webConn.UserId, previewChannel) + if !ok { // Do nothing. // In this case, the sanitized post is already attached to the ws event. return nil @@ -164,6 +165,16 @@ func (h *permalinkBroadcastHook) Process(msg *platform.HookedWebSocketEvent, web } msg.Add("post", postJSON) + auditRec := webConn.Suite.MakeAuditRecord(rctx, "websocketPost", model.AuditStatusSuccess) + defer webConn.Suite.LogAuditRec(rctx, auditRec, nil) + model.AddEventParameterToAuditRec(auditRec, "channel_id", previewChannel.Id) + model.AddEventParameterToAuditRec(auditRec, "user_id", webConn.UserId) + model.AddEventParameterToAuditRec(auditRec, "source", "permalinkBroadcastHook") + + if !isMember { + model.AddEventParameterToAuditRec(auditRec, "non_channel_member_access", true) + } + return nil } diff --git a/server/channels/app/webhook.go b/server/channels/app/webhook.go index 74b8cf1553..ea3dadcaaf 100644 --- a/server/channels/app/webhook.go +++ b/server/channels/app/webhook.go @@ -371,7 +371,7 @@ func (a *App) CreateWebhookPost(c request.CTX, userID string, channel *model.Cha } for _, split := range splits { - if _, err = a.CreatePost(c, split, channel, model.CreatePostFlags{}); err != nil { + if _, _, err := a.CreatePost(c, split, channel, model.CreatePostFlags{}); err != nil { return nil, model.NewAppError("CreateWebhookPost", "api.post.create_webhook_post.creating.app_error", nil, "", http.StatusInternalServerError).Wrap(err) } } @@ -847,7 +847,12 @@ func (a *App) HandleIncomingWebhook(c request.CTX, hookID string, req *model.Inc return model.NewAppError("HandleIncomingWebhook", "web.incoming_webhook.user.app_error", map[string]any{"user": hook.UserId}, "", http.StatusForbidden).Wrap(resultU.NErr) } - if channel.Type != model.ChannelTypeOpen && !a.HasPermissionToChannel(c, hook.UserId, channel.Id, model.PermissionReadChannelContent) { + restrictedChannel := false + if channel.Type != model.ChannelTypeOpen { + hasPermission, _ := a.HasPermissionToChannel(c, hook.UserId, channel.Id, model.PermissionReadChannelContent) + restrictedChannel = !hasPermission + } + if restrictedChannel { return model.NewAppError("HandleIncomingWebhook", "web.incoming_webhook.permissions.app_error", map[string]any{"user": hook.UserId, "channel": channel.Id}, "", http.StatusForbidden) } diff --git a/server/channels/wsapi/user.go b/server/channels/wsapi/user.go index 9761fec0b4..2087064a16 100644 --- a/server/channels/wsapi/user.go +++ b/server/channels/wsapi/user.go @@ -27,7 +27,7 @@ func (api *API) userTyping(req *model.WebSocketRequest) (map[string]any, *model. return nil, NewInvalidWebSocketParamError(req.Action, "channel_id") } - if !api.App.SessionHasPermissionToChannel(request.EmptyContext(api.App.Log()), req.Session, channelId, model.PermissionCreatePost) { + if hasPermission, _ := api.App.SessionHasPermissionToChannel(request.EmptyContext(api.App.Log()), req.Session, channelId, model.PermissionCreatePost); !hasPermission { return nil, NewInvalidWebSocketParamError(req.Action, "channel_id") } diff --git a/server/cmd/mmctl/commands/post_e2e_test.go b/server/cmd/mmctl/commands/post_e2e_test.go index 62ef4e1468..8aa929c98f 100644 --- a/server/cmd/mmctl/commands/post_e2e_test.go +++ b/server/cmd/mmctl/commands/post_e2e_test.go @@ -22,10 +22,10 @@ func (s *MmctlE2ETestSuite) TestPostListCmd() { channel, err := s.th.App.CreateChannel(s.th.Context, &model.Channel{Name: channelName, DisplayName: channelDisplayName, Type: model.ChannelTypePrivate, TeamId: s.th.BasicTeam.Id}, false) s.Require().Nil(err) - post1, err := s.th.App.CreatePost(s.th.Context, &model.Post{Message: model.NewRandomString(15), UserId: s.th.BasicUser.Id, ChannelId: channel.Id}, channel, model.CreatePostFlags{}) + post1, _, err := s.th.App.CreatePost(s.th.Context, &model.Post{Message: model.NewRandomString(15), UserId: s.th.BasicUser.Id, ChannelId: channel.Id}, channel, model.CreatePostFlags{}) s.Require().Nil(err) - post2, err := s.th.App.CreatePost(s.th.Context, &model.Post{Message: model.NewRandomString(15), UserId: s.th.BasicUser.Id, ChannelId: channel.Id}, channel, model.CreatePostFlags{}) + post2, _, err := s.th.App.CreatePost(s.th.Context, &model.Post{Message: model.NewRandomString(15), UserId: s.th.BasicUser.Id, ChannelId: channel.Id}, channel, model.CreatePostFlags{}) s.Require().Nil(err) return channelName, post1, post2 diff --git a/server/platform/services/sharedchannel/mock_AppIface_test.go b/server/platform/services/sharedchannel/mock_AppIface_test.go index 8bc2ff593a..36be5a5bae 100644 --- a/server/platform/services/sharedchannel/mock_AppIface_test.go +++ b/server/platform/services/sharedchannel/mock_AppIface_test.go @@ -142,7 +142,7 @@ func (_m *MockAppIface) CreateGroupChannel(c request.CTX, userIDs []string, crea } // CreatePost provides a mock function with given fields: c, post, channel, flags -func (_m *MockAppIface) CreatePost(c request.CTX, post *model.Post, channel *model.Channel, flags model.CreatePostFlags) (*model.Post, *model.AppError) { +func (_m *MockAppIface) CreatePost(c request.CTX, post *model.Post, channel *model.Channel, flags model.CreatePostFlags) (*model.Post, bool, *model.AppError) { ret := _m.Called(c, post, channel, flags) if len(ret) == 0 { @@ -150,8 +150,9 @@ func (_m *MockAppIface) CreatePost(c request.CTX, post *model.Post, channel *mod } 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(c, 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(c request.CTX, post *model.Post, channel *mod } } - 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(c, 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(c, 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: c, us @@ -707,7 +714,7 @@ func (_m *MockAppIface) SaveReactionForPost(c request.CTX, reaction *model.React } // SendEphemeralPost provides a mock function with given fields: c, userId, post -func (_m *MockAppIface) SendEphemeralPost(c request.CTX, userId string, post *model.Post) *model.Post { +func (_m *MockAppIface) SendEphemeralPost(c request.CTX, userId string, post *model.Post) (*model.Post, bool) { ret := _m.Called(c, userId, post) if len(ret) == 0 { @@ -715,6 +722,10 @@ func (_m *MockAppIface) SendEphemeralPost(c request.CTX, userId string, post *mo } 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(c, userId, post) + } if rf, ok := ret.Get(0).(func(request.CTX, string, *model.Post) *model.Post); ok { r0 = rf(c, userId, post) } else { @@ -723,11 +734,17 @@ func (_m *MockAppIface) SendEphemeralPost(c request.CTX, userId string, post *mo } } - return r0 + if rf, ok := ret.Get(1).(func(request.CTX, string, *model.Post) bool); ok { + r1 = rf(c, userId, post) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 } // UpdatePost provides a mock function with given fields: c, post, updatePostOptions -func (_m *MockAppIface) UpdatePost(c request.CTX, post *model.Post, updatePostOptions *model.UpdatePostOptions) (*model.Post, *model.AppError) { +func (_m *MockAppIface) UpdatePost(c request.CTX, post *model.Post, updatePostOptions *model.UpdatePostOptions) (*model.Post, bool, *model.AppError) { ret := _m.Called(c, post, updatePostOptions) if len(ret) == 0 { @@ -735,8 +752,9 @@ func (_m *MockAppIface) UpdatePost(c request.CTX, post *model.Post, updatePostOp } 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(c, 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(c request.CTX, post *model.Post, updatePostOp } } - 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(c, 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(c, 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: c, userID, otherUserId diff --git a/server/platform/services/sharedchannel/permalink_test.go b/server/platform/services/sharedchannel/permalink_test.go index 53e5954136..201c5db000 100644 --- a/server/platform/services/sharedchannel/permalink_test.go +++ b/server/platform/services/sharedchannel/permalink_test.go @@ -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) { diff --git a/server/platform/services/sharedchannel/service.go b/server/platform/services/sharedchannel/service.go index cde1e9254e..25e6c2334d 100644 --- a/server/platform/services/sharedchannel/service.go +++ b/server/platform/services/sharedchannel/service.go @@ -54,7 +54,7 @@ type PlatformIface interface { } type AppIface interface { - SendEphemeralPost(c request.CTX, userId string, post *model.Post) *model.Post + SendEphemeralPost(c request.CTX, userId string, post *model.Post) (*model.Post, bool) CreateChannelWithUser(c request.CTX, channel *model.Channel, userId string) (*model.Channel, *model.AppError) GetOrCreateDirectChannel(c request.CTX, userId, otherUserId string, channelOptions ...model.ChannelOption) (*model.Channel, *model.AppError) CreateGroupChannel(c request.CTX, userIDs []string, creatorId string, channelOptions ...model.ChannelOption) (*model.Channel, *model.AppError) @@ -63,8 +63,8 @@ type AppIface interface { AddUserToTeamByTeamId(c request.CTX, teamId string, user *model.User) *model.AppError RemoveUserFromChannel(c request.CTX, userID string, removerUserId string, channel *model.Channel) *model.AppError PermanentDeleteChannel(c request.CTX, channel *model.Channel) *model.AppError - CreatePost(c request.CTX, post *model.Post, channel *model.Channel, flags model.CreatePostFlags) (savedPost *model.Post, err *model.AppError) - UpdatePost(c request.CTX, post *model.Post, updatePostOptions *model.UpdatePostOptions) (*model.Post, *model.AppError) + CreatePost(c request.CTX, post *model.Post, channel *model.Channel, flags model.CreatePostFlags) (savedPost *model.Post, isMemberForPreviews bool, err *model.AppError) + UpdatePost(c request.CTX, post *model.Post, updatePostOptions *model.UpdatePostOptions) (*model.Post, bool, *model.AppError) DeletePost(c request.CTX, postID, deleteByID string) (*model.Post, *model.AppError) SaveReactionForPost(c request.CTX, reaction *model.Reaction) (*model.Reaction, *model.AppError) DeleteReactionForPost(c 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( diff --git a/server/platform/services/sharedchannel/sync_recv.go b/server/platform/services/sharedchannel/sync_recv.go index 41f2e7ccab..01ea4cc372 100644 --- a/server/platform/services/sharedchannel/sync_recv.go +++ b/server/platform/services/sharedchannel/sync_recv.go @@ -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