修复微信绑定过期取消并补充会话隔离回归用例
Some checks are pending
Security Checks / security (push) Waiting to run

This commit is contained in:
smail 2026-09-28 21:53:23 +08:00
parent a62987c4b3
commit fa53f7c641
3 changed files with 43 additions and 2 deletions

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"time" "time"
@ -202,6 +203,40 @@ func TestWeChatTestMessagesAreRateLimitedPerAccount(t *testing.T) {
} }
} }
func TestWeChatBindingCancellationAllowsExpiredAndMissingTickets(t *testing.T) {
service, engine := newPluginConfigService(t)
user := insertPluginTestUser(t, engine, "wechat-cancel", access.RoleSuperAdmin)
actor := NotificationBindingActor{UserID: user.ID, SessionHash: strings.Repeat("a", 64),
SessionVersion: user.SessionVersion, SecurityVersion: user.SecurityVersion}
service.wechatBindings = map[string]weChatBinding{
"expired": {Actor: actor, Status: WeChatBindingStatus{ExpiresAt: time.Now().Add(-time.Minute)}},
}
otherActor := actor
otherActor.SessionHash = strings.Repeat("b", 64)
if err := service.CancelWeChatBinding(otherActor, "expired"); !errors.Is(err, ErrWeChatBinding) {
t.Fatalf("不得撤销其他会话的绑定:%v", err)
}
if len(service.wechatBindings) != 1 {
t.Fatal("其他会话的取消请求不应删除原绑定")
}
if err := service.CancelWeChatBinding(actor, "expired"); err != nil {
t.Fatalf("过期请求应允许取消:%v", err)
}
if len(service.wechatBindings) != 0 {
t.Fatal("已取消的绑定不应残留")
}
if err := service.CancelWeChatBinding(actor, "expired"); err != nil {
t.Fatalf("重复取消应保持幂等:%v", err)
}
if err := service.CancelWeChatBinding(actor, "missing"); err != nil {
t.Fatalf("不存在的请求应允许关闭:%v", err)
}
actor.SecurityVersion++
if err := service.CancelWeChatBinding(actor, "missing"); !errors.Is(err, ErrWeChatBinding) {
t.Fatalf("幂等取消仍必须验证当前账户和会话:%v", err)
}
}
func TestEveryRoleKeepsIndependentOwnTelegramSettings(t *testing.T) { func TestEveryRoleKeepsIndependentOwnTelegramSettings(t *testing.T) {
service, engine := newPluginConfigService(t) service, engine := newPluginConfigService(t)
var users []models.User var users []models.User

View File

@ -152,12 +152,16 @@ func (s *ConfigService) GetWeChatBinding(actor NotificationBindingActor, ticket
} }
func (s *ConfigService) CancelWeChatBinding(actor NotificationBindingActor, ticket string) error { func (s *ConfigService) CancelWeChatBinding(actor NotificationBindingActor, ticket string) error {
if _, err := s.GetWeChatBinding(actor, ticket); err != nil { if err := s.validateBindingActor(actor); err != nil {
return err return err
} }
s.bindingMu.Lock() s.bindingMu.Lock()
defer s.bindingMu.Unlock()
// 过期、回调失败或重复取消都允许关闭页面,但不得撤销其他会话的请求。
if binding, found := s.wechatBindings[ticket]; found && binding.Actor != actor {
return ErrWeChatBinding
}
delete(s.wechatBindings, ticket) delete(s.wechatBindings, ticket)
s.bindingMu.Unlock()
return nil return nil
} }

View File

@ -121,6 +121,8 @@ test('未知插件只使用安全名称生成隔离页面,内置插件别名
assert.doesNotMatch(template, /allow-same-origin|v-for=".*pluginEntries/) assert.doesNotMatch(template, /allow-same-origin|v-for=".*pluginEntries/)
assert.match(template, /:key="selectedSection"[\s\S]*:page-key="selectedSection"/) assert.match(template, /:key="selectedSection"[\s\S]*:page-key="selectedSection"/)
assert.match(template, /defineExpose\(\{ beforeLeave \}\)/) assert.match(template, /defineExpose\(\{ beforeLeave \}\)/)
assert.match(template, /<TelegramSettings v-else-if="activePage\?\.value === 'telegram-user'"/)
assert.match(template, /<WeChatSettings v-else-if="activePage\?\.value === 'wechat-user'"/)
}) })
test('插件列表故障不隐藏三个运行配置页,支持重试并丢弃卸载后的响应', async () => { test('插件列表故障不隐藏三个运行配置页,支持重试并丢弃卸载后的响应', async () => {