fix: resolve P1 backend consistency issues
This commit is contained in:
+87
-7
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user