fix: resolve P1 backend consistency issues

This commit is contained in:
richjohnrambo
2026-09-08 16:23:21 +08:00
parent 06945a4050
commit 0b3c0b1c78
13 changed files with 587 additions and 57 deletions
+87 -7
View File
@@ -12,6 +12,7 @@ import (
"context"
"errors"
"sort"
"strconv"
"strings"
"sync/atomic"
"time"
@@ -1215,6 +1216,18 @@ func (t *Topic) handlePubBroadcast(msg *ClientComMessage) {
msg.sess.queueOut(ErrPermissionDenied(msg.Id, t.original(asUid), msg.Timestamp))
return
}
if err := t.validateMessageReplacement(asUid, msg.Pub.Head, msg.Timestamp); err != nil {
if errors.Is(err, types.ErrMalformed) {
msg.sess.queueOut(ErrMalformedReply(msg, types.TimeNow()))
} else {
msg.sess.queueOut(ErrPermissionDeniedReply(msg, types.TimeNow()))
}
return
}
if err := t.consumeRiskActionToken(asUid, msg.Pub.Head); err != nil {
msg.sess.queueOut(ErrPermissionDeniedReply(msg, types.TimeNow()))
return
}
isCall := msg.Pub.Head != nil && msg.Pub.Head["webrtc"] != nil
if isCall {
@@ -1262,16 +1275,67 @@ func (t *Topic) handlePubBroadcast(msg *ClientComMessage) {
}
}
func (t *Topic) validateMessageReplacement(asUid types.Uid, head map[string]any, now time.Time) error {
if head == nil || head["replace"] == nil {
return nil
}
replace, ok := head["replace"].(string)
if !ok {
return types.ErrMalformed
}
seq, err := strconv.ParseInt(strings.TrimPrefix(strings.TrimSpace(replace), ":"), 10, 64)
if err != nil || seq <= 0 {
return types.ErrMalformed
}
original, err := store.Messages.GetOne(t.tenantID, t.name, seq)
if err != nil {
return err
}
if original == nil || types.ParseUid(original.From) != asUid {
return types.ErrPermissionDenied
}
if now.IsZero() {
now = types.TimeNow()
}
if original.CreatedAt.IsZero() || now.Sub(original.CreatedAt) > 5*time.Minute {
return types.ErrPermissionDenied
}
return nil
}
func (t *Topic) consumeRiskActionToken(asUid types.Uid, head map[string]any) error {
if head == nil {
return nil
}
token, _ := head["risk_action_token"].(string)
challengeID, _ := head["risk_challenge_id"].(string)
token = strings.TrimSpace(token)
challengeID = strings.TrimSpace(challengeID)
if token == "" && challengeID == "" {
return nil
}
if token == "" || challengeID == "" || store.Security == nil {
return types.ErrPermissionDenied
}
if _, err := store.Security.ConsumeActionToken(t.tenantID, asUid, token, "risk_continue", challengeID, types.TimeNow()); err != nil {
return err
}
delete(head, "risk_action_token")
return nil
}
func (t *Topic) validateAndNormalizeMentions(asUid types.Uid, head map[string]any) (map[string]any, error) {
mentionAll, _ := head["mention_all"].(bool)
rawMentions, hasMentions := head["mentions"]
if !mentionAll && !hasMentions {
return head, nil
}
pud := t.perUser[asUid]
access := pud.modeGiven & pud.modeWant
if mentionAll && !access.IsAdmin() {
return nil, types.ErrPermissionDenied
if mentionAll {
if sub, err := store.Subs.Get(t.tenantID, t.name, asUid, false); err != nil {
return nil, err
} else if sub == nil || !(sub.ModeGiven & sub.ModeWant).IsAdmin() {
return nil, types.ErrPermissionDenied
}
}
validMembers := make(map[string]struct{}, len(t.perUser))
for uid, pud := range t.perUser {
@@ -2318,9 +2382,25 @@ func (t *Topic) replyGetDesc(sess *Session, asUid types.Uid, _ bool, opts *MsgGe
desc.Public = t.public
desc.Trusted = t.trusted
} else if full && t.cat == types.TopicCatP2P {
// FIXME: when a P2P participant updates desc at 'me', these cached values are not updated.
desc.Public = pud.public
desc.Trusted = pud.trusted
uid1, uid2, err := types.ParseP2P(t.name)
if err != nil {
return err
}
peer := uid1
if peer == asUid {
peer = uid2
}
user, err := store.Users.Get(t.tenantID, peer)
if err != nil {
return err
}
if user != nil {
desc.Public = user.Public
desc.Trusted = user.Trusted
} else {
desc.Public = pud.public
desc.Trusted = pud.trusted
}
}
}