补充公众号变更后的个人重绑状态和发送限制
Some checks are pending
Security Checks / security (push) Waiting to run

公众号身份不匹配时阻止重新启用、测试发送与邮件通知,保留停用及解绑;新增身份匹配回归源码。全量编译和相关包静态检查通过,未执行回归和实机验收。作为修复全量静态检查发现的既有用例签名错误前检查点。
This commit is contained in:
smail 2026-09-28 21:31:37 +08:00
parent afc177132a
commit 7b6891fdbb
4 changed files with 56 additions and 7 deletions

View File

@ -57,7 +57,8 @@ func weChatRequestService(ctx *appcontext.Context, w http.ResponseWriter, recent
func weChatPublicSettings(status pluginservice.WeChatUserSettingsStatus) map[string]any {
return map[string]any{"version": status.Version, "bound": status.Settings.OpenID != "", "enabled": status.Settings.Enabled,
"showContent": status.Settings.ShowContent, "displayName": status.Settings.DisplayName}
"showContent": status.Settings.ShowContent, "displayName": status.Settings.DisplayName,
"needsRebind": status.NeedsRebind}
}
func writeWeChatSettings(w http.ResponseWriter, userID int, status pluginservice.WeChatUserSettingsStatus) {
@ -175,7 +176,7 @@ func TestWeChatMessage(ctx *appcontext.Context, w http.ResponseWriter, req *http
return
}
status, err := service.GetOwnWeChatSettings(ctx.UserID)
if err != nil || status.Settings.OpenID == "" {
if err != nil || status.Settings.OpenID == "" || status.NeedsRebind {
writeWeChatError(w, pluginservice.WeChatNotice("请先绑定本人的微信"))
return
}

View File

@ -129,7 +129,7 @@ func (h *HookSender) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Ema
}
// 发信前重新读取,停用、解绑或禁用账户后不能继续使用插件缓存中的旧接收人。
status, err := pluginConfigService.GetOwnWeChatSettings(recipient.UserID)
if err != nil || !status.Settings.Enabled || status.Settings.OpenID == "" {
if err != nil || !status.Settings.Enabled || status.Settings.OpenID == "" || status.NeedsRebind {
continue
}
payload, err := json.Marshal(status.Settings)

View File

@ -127,6 +127,28 @@ func TestEveryRoleKeepsIndependentOwnWeChatSettings(t *testing.T) {
}
}
func TestWeChatBindingIdentityMustMatchCurrentApplication(t *testing.T) {
for _, test := range []struct {
name string
settings WeChatUserSettings
appID string
want bool
}{
{name: "unbound"},
{name: "same-app", settings: WeChatUserSettings{AppID: "wx-original-app", OpenID: "openid-test-user", Enabled: true}, appID: "wx-original-app"},
{name: "changed-app", settings: WeChatUserSettings{AppID: "wx-original-app", OpenID: "openid-test-user", Enabled: true}, appID: "wx-replacement-app", want: true},
{name: "missing-app", settings: WeChatUserSettings{AppID: "wx-original-app", OpenID: "openid-test-user"}, want: true},
{name: "invalid-app", settings: WeChatUserSettings{AppID: "wx-original-app", OpenID: "openid-test-user"}, appID: "invalid", want: true},
{name: "disabled-same-app", settings: WeChatUserSettings{AppID: "wx-original-app", OpenID: "openid-test-user"}, appID: "wx-original-app"},
} {
t.Run(test.name, func(t *testing.T) {
if got := weChatBindingNeedsRebind(test.settings, test.appID); got != test.want {
t.Fatal(got, test.want)
}
})
}
}
func TestWeChatBindingStatusRejectsOtherSessionsAndRevokedAccounts(t *testing.T) {
service, engine := newPluginConfigService(t)
user := insertPluginTestUser(t, engine, "wechat-binding", access.RoleSuperAdmin)

View File

@ -5,6 +5,7 @@ import (
"errors"
"strings"
"git.pcux.cn/xuans/Smail/config"
"git.pcux.cn/xuans/Smail/models"
"git.pcux.cn/xuans/Smail/services/access"
"git.pcux.cn/xuans/Smail/utils/credential"
@ -29,8 +30,9 @@ type WeChatUserSettings struct {
}
type WeChatUserSettingsStatus struct {
Settings WeChatUserSettings
Version uint64
Settings WeChatUserSettings
Version uint64
NeedsRebind bool
}
type WeChatUserSettingsEntry struct {
@ -61,7 +63,27 @@ func (s *ConfigService) GetOwnWeChatSettings(userID int) (WeChatUserSettingsStat
return WeChatUserSettingsStatus{}, err
}
settings, err := s.decodeWeChatSettings(record)
return WeChatUserSettingsStatus{Settings: settings, Version: record.Version}, err
return WeChatUserSettingsStatus{Settings: settings, Version: record.Version,
NeedsRebind: weChatBindingNeedsRebind(settings, currentWeChatAppID())}, err
}
func currentWeChatAppID() string {
manager := config.CurrentManager()
if manager == nil {
return ""
}
instance, found := manager.Snapshot().Plugins.Plugins[weChatPluginID]
var settings struct {
AppID string `json:"appId"`
}
if !found || json.Unmarshal(instance.Settings, &settings) != nil {
return ""
}
return settings.AppID
}
func weChatBindingNeedsRebind(settings WeChatUserSettings, appID string) bool {
return settings.OpenID != "" && (!validWeChatIdentity(appID) || settings.AppID != appID)
}
func (s *ConfigService) decodeWeChatSettings(record models.PluginUserSetting) (WeChatUserSettings, error) {
@ -147,7 +169,8 @@ func (s *ConfigService) saveWeChatSettings(userID int, settings WeChatUserSettin
return WeChatUserSettingsStatus{}, ErrPluginSettingConflict
}
}
return WeChatUserSettingsStatus{Settings: settings, Version: record.Version}, nil
return WeChatUserSettingsStatus{Settings: settings, Version: record.Version,
NeedsRebind: weChatBindingNeedsRebind(settings, currentWeChatAppID())}, nil
}
func (s *ConfigService) UpdateOwnWeChatPreferences(userID int, enabled, showContent, unbind bool, expectedVersion uint64) (WeChatUserSettingsStatus, error) {
@ -156,6 +179,9 @@ func (s *ConfigService) UpdateOwnWeChatPreferences(userID int, enabled, showCont
return current, err
}
settings := current.Settings
if !unbind && enabled && current.NeedsRebind {
return current, ErrWeChatBinding
}
if unbind {
settings = WeChatUserSettings{}
} else {