fix: harden channel rights and sticker compatibility

Co-authored-by: HSgram <3013954224@qq.com>
This commit is contained in:
A 2026-07-01 22:28:24 +08:00
parent 6867d201ed
commit 599453a3c4
33 changed files with 1520 additions and 130 deletions

View file

@ -423,7 +423,7 @@ func channelReplyBelongsToRoot(msg domain.ChannelMessage, channelID int64, rootI
return msg.ReplyTo.TopMessageID == rootID || (msg.ReplyTo.TopMessageID == 0 && msg.ReplyTo.MessageID == rootID)
}
func (s *ChannelStore) resolveChannelReplyLocked(req domain.SendChannelMessageRequest, member domain.ChannelMember, channel domain.Channel) (*domain.MessageReply, error) {
func (s *ChannelStore) resolveChannelReplyLocked(req domain.SendChannelMessageRequest, member domain.ChannelMember, channel domain.Channel, selfBoostsApplied int) (*domain.MessageReply, error) {
if req.ReplyTo == nil {
return nil, nil
}
@ -446,7 +446,7 @@ func (s *ChannelStore) resolveChannelReplyLocked(req domain.SendChannelMessageRe
if !ok || topic.Hidden {
return nil, domain.ErrReplyMessageIDInvalid
}
if topic.Closed && !canManageForumTopic(channel, member, topic, req.UserID) {
if topic.Closed && !canManageForumTopic(channel, member, topic, req.UserID, selfBoostsApplied) {
return nil, domain.ErrChannelWriteForbidden
}
reply := cloneMessageReply(req.ReplyTo)
@ -472,7 +472,7 @@ func (s *ChannelStore) resolveChannelReplyLocked(req domain.SendChannelMessageRe
}
if channel.Forum && reply.TopMessageID > 0 {
if topic, ok := s.topics[req.ChannelID][reply.TopMessageID]; ok && !topic.Hidden {
if topic.Closed && !canManageForumTopic(channel, member, topic, req.UserID) {
if topic.Closed && !canManageForumTopic(channel, member, topic, req.UserID, selfBoostsApplied) {
return nil, domain.ErrChannelWriteForbidden
}
reply.ForumTopic = true

View file

@ -28,6 +28,9 @@ func (s *ChannelStore) SendChannelMessage(_ context.Context, req domain.SendChan
if channel.Megagroup {
fromBoostsApplied = s.selfBoostsAppliedLocked(req.UserID, req.ChannelID, req.Date)
}
if domain.ChannelBannedRightsBlockMessage(req, channel, member, fromBoostsApplied) {
return domain.SendChannelMessageResult{}, domain.ErrChannelWriteForbidden
}
if !canSendChannelMessageWithBoost(channel, member, fromBoostsApplied) {
return domain.SendChannelMessageResult{}, domain.ErrChannelWriteForbidden
}
@ -51,7 +54,7 @@ func (s *ChannelStore) SendChannelMessage(_ context.Context, req domain.SendChan
if wait := channelSlowModeWait(channel, member, req.Date); wait > 0 {
return domain.SendChannelMessageResult{}, domain.NewSlowModeWaitError(wait)
}
replyTo, err := s.resolveChannelReplyLocked(req, member, channel)
replyTo, err := s.resolveChannelReplyLocked(req, member, channel, fromBoostsApplied)
if err != nil {
return domain.SendChannelMessageResult{}, err
}

View file

@ -50,6 +50,15 @@ func (s *ChannelStore) SetChannelMessageReactions(_ context.Context, req domain.
if err != nil {
return domain.ChannelMessageReactionsResult{}, err
}
if len(req.Reactions) > 0 {
selfBoostsApplied := 0
if channel.Megagroup {
selfBoostsApplied = s.selfBoostsAppliedLocked(req.UserID, req.ChannelID, req.Date)
}
if domain.ChannelBannedRightsBlockReactions(channel, member, selfBoostsApplied) {
return domain.ChannelMessageReactionsResult{}, domain.ErrChannelWriteForbidden
}
}
idx, ok := s.findMessageIndexLocked(req.ChannelID, req.MessageID)
if !ok {
return domain.ChannelMessageReactionsResult{}, domain.ErrMessageIDInvalid

View file

@ -92,6 +92,18 @@ func (s *ChannelStore) CreateForumTopic(ctx context.Context, req domain.CreateCh
s.mu.Unlock()
return domain.CreateChannelForumTopicResult{}, domain.ErrChannelWriteForbidden
}
selfBoostsApplied := 0
if channel.Megagroup {
now := req.Date
if now == 0 {
now = int(time.Now().Unix())
}
selfBoostsApplied = s.selfBoostsAppliedLocked(req.UserID, req.ChannelID, now)
}
if domain.ChannelBannedRightsBlockManageTopics(channel, member, selfBoostsApplied) {
s.mu.Unlock()
return domain.CreateChannelForumTopicResult{}, domain.ErrChannelWriteForbidden
}
if id, ok := s.randomToID[channelRandomKey{channelID: req.ChannelID, userID: req.UserID, randomID: req.RandomID}]; ok {
if topic, ok := s.topics[req.ChannelID][id]; ok {
msg, _ := s.findMessageLocked(req.ChannelID, id)
@ -184,7 +196,15 @@ func (s *ChannelStore) EditForumTopic(ctx context.Context, req domain.EditChanne
s.mu.Unlock()
return domain.EditChannelForumTopicResult{}, domain.ErrMessageIDInvalid
}
if !canManageForumTopic(channel, member, topic, req.UserID) {
selfBoostsApplied := 0
if channel.Megagroup {
now := req.Date
if now == 0 {
now = int(time.Now().Unix())
}
selfBoostsApplied = s.selfBoostsAppliedLocked(req.UserID, req.ChannelID, now)
}
if !canManageForumTopic(channel, member, topic, req.UserID, selfBoostsApplied) {
s.mu.Unlock()
return domain.EditChannelForumTopicResult{}, domain.ErrChannelAdminRequired
}
@ -361,7 +381,7 @@ func (s *ChannelStore) DeleteForumTopicHistory(_ context.Context, req domain.Del
if !ok {
return domain.DeleteChannelHistoryResult{}, domain.ErrMessageIDInvalid
}
if !canManageForumTopic(channel, member, topic, req.UserID) && !canDeleteAnyChannelMessage(member) {
if !canManageForumTopic(channel, member, topic, req.UserID, 0) && !canDeleteAnyChannelMessage(member) {
return domain.DeleteChannelHistoryResult{}, domain.ErrChannelAdminRequired
}
ids := make([]int, 0, domain.MaxDeleteHistoryBatch)
@ -613,7 +633,10 @@ func (s *ChannelStore) channelMessageRepliesLocked(viewerUserID, channelID int64
return &stats
}
func canManageForumTopic(channel domain.Channel, member domain.ChannelMember, topic domain.ChannelForumTopic, userID int64) bool {
func canManageForumTopic(channel domain.Channel, member domain.ChannelMember, topic domain.ChannelForumTopic, userID int64, selfBoostsApplied int) bool {
if domain.ChannelBannedRightsBlockManageTopics(channel, member, selfBoostsApplied) {
return false
}
if topic.CreatorUserID == userID {
return true
}

View file

@ -653,7 +653,7 @@ GROUP BY topic_id`, userID, channelID, roots, availableMinID)
return nil
}
func (s *ChannelStore) resolveChannelReply(ctx context.Context, db sqlcgen.DBTX, req domain.SendChannelMessageRequest, member domain.ChannelMember, channel domain.Channel) (*domain.MessageReply, error) {
func (s *ChannelStore) resolveChannelReply(ctx context.Context, db sqlcgen.DBTX, req domain.SendChannelMessageRequest, member domain.ChannelMember, channel domain.Channel, selfBoostsApplied int) (*domain.MessageReply, error) {
if req.ReplyTo == nil {
return nil, nil
}
@ -679,7 +679,7 @@ func (s *ChannelStore) resolveChannelReply(ctx context.Context, db sqlcgen.DBTX,
if topic.Hidden {
return nil, domain.ErrReplyMessageIDInvalid
}
if topic.Closed && !canManageForumTopic(channel, member, topic, req.UserID) {
if topic.Closed && !canManageForumTopic(channel, member, topic, req.UserID, selfBoostsApplied) {
return nil, domain.ErrChannelWriteForbidden
}
reply := cloneMessageReply(req.ReplyTo)
@ -711,7 +711,7 @@ func (s *ChannelStore) resolveChannelReply(ctx context.Context, db sqlcgen.DBTX,
}
if channel.Forum && reply.TopMessageID > 0 {
if topic, err := s.getForumTopic(ctx, db, req.ChannelID, reply.TopMessageID); err == nil && !topic.Hidden {
if topic.Closed && !canManageForumTopic(channel, member, topic, req.UserID) {
if topic.Closed && !canManageForumTopic(channel, member, topic, req.UserID, selfBoostsApplied) {
return nil, domain.ErrChannelWriteForbidden
}
reply.ForumTopic = true

View file

@ -72,7 +72,17 @@ func (s *ChannelStore) GetParticipants(ctx context.Context, viewerUserID, channe
(m.banned_rights->>'SendPolls')::boolean IS TRUE OR
(m.banned_rights->>'ChangeInfo')::boolean IS TRUE OR
(m.banned_rights->>'InviteUsers')::boolean IS TRUE OR
(m.banned_rights->>'PinMessages')::boolean IS TRUE
(m.banned_rights->>'PinMessages')::boolean IS TRUE OR
(m.banned_rights->>'ManageTopics')::boolean IS TRUE OR
(m.banned_rights->>'SendPhotos')::boolean IS TRUE OR
(m.banned_rights->>'SendVideos')::boolean IS TRUE OR
(m.banned_rights->>'SendRoundvideos')::boolean IS TRUE OR
(m.banned_rights->>'SendAudios')::boolean IS TRUE OR
(m.banned_rights->>'SendVoices')::boolean IS TRUE OR
(m.banned_rights->>'SendDocs')::boolean IS TRUE OR
(m.banned_rights->>'SendPlain')::boolean IS TRUE OR
(m.banned_rights->>'EditRank')::boolean IS TRUE OR
(m.banned_rights->>'SendReactions')::boolean IS TRUE
)`)
case domain.ChannelParticipantsSearch:
where = append(where, "m.status = 'active'")

View file

@ -61,10 +61,13 @@ func (s *ChannelStore) sendChannelMessageOnce(ctx context.Context, req domain.Se
return domain.SendChannelMessageResult{}, err
}
}
if domain.ChannelBannedRightsBlockMessage(req, channel, member, fromBoostsApplied) {
return domain.SendChannelMessageResult{}, domain.ErrChannelWriteForbidden
}
if !canSendChannelMessageWithBoost(channel, member, fromBoostsApplied) {
return domain.SendChannelMessageResult{}, domain.ErrChannelWriteForbidden
}
replyTo, err := s.resolveChannelReply(ctx, tx, req, member, channel)
replyTo, err := s.resolveChannelReply(ctx, tx, req, member, channel, fromBoostsApplied)
if err != nil {
return domain.SendChannelMessageResult{}, err
}

View file

@ -41,6 +41,18 @@ func (s *ChannelStore) SetChannelMessageReactions(ctx context.Context, req domai
if err != nil {
return domain.ChannelMessageReactionsResult{}, err
}
if len(req.Reactions) > 0 {
selfBoostsApplied := 0
if channel.Megagroup {
selfBoostsApplied, err = countActiveUserBoostsForPeer(ctx, tx, req.UserID, domain.Peer{Type: domain.PeerTypeChannel, ID: req.ChannelID}, req.Date)
if err != nil {
return domain.ChannelMessageReactionsResult{}, err
}
}
if domain.ChannelBannedRightsBlockReactions(channel, member, selfBoostsApplied) {
return domain.ChannelMessageReactionsResult{}, domain.ErrChannelWriteForbidden
}
}
msg, err := s.getChannelMessage(ctx, tx, req.ChannelID, req.MessageID)
if err != nil {
return domain.ChannelMessageReactionsResult{}, err

View file

@ -131,6 +131,20 @@ func (s *ChannelStore) CreateForumTopic(ctx context.Context, req domain.CreateCh
if !canSendChannelMessage(channel, member) {
return domain.CreateChannelForumTopicResult{}, domain.ErrChannelWriteForbidden
}
selfBoostsApplied := 0
if channel.Megagroup {
now := req.Date
if now <= 0 {
now = nowUnix()
}
selfBoostsApplied, err = s.countActiveUserBoostsForPeer(ctx, s.db, req.UserID, domain.Peer{Type: domain.PeerTypeChannel, ID: req.ChannelID}, now)
if err != nil {
return domain.CreateChannelForumTopicResult{}, err
}
}
if domain.ChannelBannedRightsBlockManageTopics(channel, member, selfBoostsApplied) {
return domain.CreateChannelForumTopicResult{}, domain.ErrChannelWriteForbidden
}
if req.IconColor == 0 {
req.IconColor = domain.DefaultForumTopicIconColor
}
@ -192,7 +206,18 @@ func (s *ChannelStore) EditForumTopic(ctx context.Context, req domain.EditChanne
if err != nil {
return domain.EditChannelForumTopicResult{}, err
}
if !canManageForumTopic(channel, member, topic, req.UserID) {
selfBoostsApplied := 0
if channel.Megagroup {
now := req.Date
if now <= 0 {
now = nowUnix()
}
selfBoostsApplied, err = s.countActiveUserBoostsForPeer(ctx, s.db, req.UserID, domain.Peer{Type: domain.PeerTypeChannel, ID: req.ChannelID}, now)
if err != nil {
return domain.EditChannelForumTopicResult{}, err
}
}
if !canManageForumTopic(channel, member, topic, req.UserID, selfBoostsApplied) {
return domain.EditChannelForumTopicResult{}, domain.ErrChannelAdminRequired
}
next := topic
@ -390,7 +415,7 @@ func (s *ChannelStore) DeleteForumTopicHistory(ctx context.Context, req domain.D
if err != nil {
return domain.DeleteChannelHistoryResult{}, err
}
if !canManageForumTopic(channel, member, topic, req.UserID) && !canDeleteAnyChannelMessage(member) {
if !canManageForumTopic(channel, member, topic, req.UserID, 0) && !canDeleteAnyChannelMessage(member) {
return domain.DeleteChannelHistoryResult{}, domain.ErrChannelAdminRequired
}
rows, err := tx.Query(ctx, `
@ -1023,7 +1048,10 @@ func scanChannelForumTopic(row rowScanner) (domain.ChannelForumTopic, error) {
return topic, nil
}
func canManageForumTopic(channel domain.Channel, member domain.ChannelMember, topic domain.ChannelForumTopic, userID int64) bool {
func canManageForumTopic(channel domain.Channel, member domain.ChannelMember, topic domain.ChannelForumTopic, userID int64, selfBoostsApplied int) bool {
if domain.ChannelBannedRightsBlockManageTopics(channel, member, selfBoostsApplied) {
return false
}
if topic.CreatorUserID == userID {
return true
}