Smail/test/backend/plugin_role_scope_test.go
smail 7e007ea891
Some checks are pending
Security Checks / security (push) Waiting to run
安全码计时复审前保存权限回归修正
2026-10-06 07:36:46 +08:00

116 lines
5.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package config
import (
"context"
"encoding/json"
"errors"
"testing"
"git.pcux.cn/xuans/Smail/services/access"
appcontext "git.pcux.cn/xuans/Smail/utils/context"
log "github.com/sirupsen/logrus"
)
func TestPluginRolesValidateUpdateAndPending(t *testing.T) {
root := t.TempDir()
fixture := validRuntimeConfig()
fixture.Plugins.Plugins["spam_block"] = PluginInstance{
Executable: currentPluginExecutable("spam_block"),
Permissions: PluginPermissions{Capabilities: []string{"network"}},
Settings: json.RawMessage("{\"score\":1}"),
}
writeRuntimeFixture(t, root, fixture)
manager, err := NewConfigManagerWithSecurity(root, ConfigLoaderOptions{Validation: RuntimeValidationOptions{HasSuperAdmin: true, Production: true}}, ConfigSecurity{Audit: &MemoryAuditWriter{}})
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
admin := AuditActor{UserID: "2", Role: "admin", SessionID: "admin-session", RecentAuth: true, RecoveryCodeConfigured: true}
current := manager.Snapshot()
for _, change := range []string{"executable", "permissions", "install", "remove", "settings"} {
candidate := cloneRuntimeConfig(current)
plugin := candidate.Plugins.Plugins["telegram_push"]
switch change {
case "executable":
plugin.Executable = "other-program.exe"
case "permissions":
plugin.Permissions.Network[0].Host = "other.example.com"
case "install":
candidate.Plugins.Plugins["other"] = plugin
case "settings":
plugin.Settings = json.RawMessage("{\"timeout\":30}")
}
candidate.Plugins.Plugins["telegram_push"] = plugin
if change == "remove" {
delete(candidate.Plugins.Plugins, "telegram_push")
}
payload, _ := json.Marshal(candidate.Plugins)
if _, err := manager.ValidateAuthorized(ctx, ValidateRequest{Section: SectionPlugins, ExpectedGeneration: current.Generation, Payload: payload}, admin); !errors.Is(err, ErrAuthorizationRequired) {
t.Fatalf("校验允许越权 %s:%v", change, err)
}
if _, err := manager.Update(ctx, UpdateRequest{Section: SectionPlugins, ExpectedGeneration: current.Generation, Payload: payload, Actor: admin}); !errors.Is(err, ErrAuthorizationRequired) {
t.Fatalf("保存允许越权 %s:%v", change, err)
}
}
plugins := cloneRuntimeConfig(current).Plugins
plugin := plugins.Plugins["spam_block"]
plugin.Settings = json.RawMessage(`{"displayName":"修改通知名称"}`)
plugins.Plugins["spam_block"] = plugin
payload, _ := json.Marshal(plugins)
validation, err := manager.ValidateAuthorized(ctx, ValidateRequest{Section: SectionPlugins, ExpectedGeneration: current.Generation, Payload: payload}, admin)
if err != nil || !validation.Valid {
t.Fatalf("管理员参数校验失败:%+v %v", validation, err)
}
updated, err := manager.Update(ctx, UpdateRequest{Section: SectionPlugins, ExpectedGeneration: current.Generation, Payload: payload, Actor: admin})
if err != nil {
t.Fatal(err)
}
other := admin
other.UserID, other.SessionID = "3", "other-session"
user := AuditActor{UserID: "4", Role: "user", SessionID: "user-session", RecentAuth: true}
for _, actor := range []AuditActor{other, user} {
if manager.Pending(actor) != nil {
t.Fatal("读取了其他操作者候选")
}
if _, err := manager.ApplyPendingAuthorized(ctx, updated.Generation, updated.CandidateID, SectionPlugins, actor); err == nil {
t.Fatal("应用了其他操作者候选")
}
if err := manager.ManagePending(ctx, updated.Generation, updated.CandidateID, actor, StepUpConfirmation{Confirmed: true, Method: "recent_auth"}, true); err == nil {
t.Fatal("取消了其他操作者候选")
}
}
if view := manager.Pending(admin); view == nil || !view.SameSession {
t.Fatal("本人候选不可见")
}
if _, err := manager.ApplyPendingAuthorized(ctx, updated.Generation, updated.CandidateID, SectionRoot, admin); err == nil {
t.Fatal("候选可跨分区应用")
}
if _, err := manager.ApplyPendingAuthorized(ctx, updated.Generation, updated.CandidateID, SectionPlugins, admin); err != nil {
t.Fatal(err)
}
super := authorizedConfigActor()
mail, _ := json.Marshal(manager.Snapshot().Email)
if _, err := manager.ValidateAuthorized(ctx, ValidateRequest{Section: SectionEmail, Payload: mail}, admin); err == nil {
t.Fatal("管理员可校验系统配置")
}
if _, err := manager.Update(ctx, UpdateRequest{Section: SectionEmail, ExpectedGeneration: manager.Snapshot().Generation, Payload: mail, Actor: super}); err != nil {
t.Fatal(err)
}
if manager.Pending(admin) != nil {
t.Fatal("管理员读到了系统配置候选")
}
}
func TestRuntimeActorCannotBeSuppliedByLogFields(t *testing.T) {
entry := &log.Entry{Data: log.Fields{"actorId": "1", "actorRole": "super_admin"}}
fields := runtimeEventFields(entry)
if fields["actorId"] != nil || fields["actorRole"] != nil {
t.Fatal("任意日志字段可伪造归属")
}
entry.Context = &appcontext.Context{UserID: 4, Role: access.RoleUser}
fields = runtimeEventFields(entry)
if fields["actorId"] != "4" || fields["actorRole"] != "user" || entry.Data["actorId"] != "1" {
t.Fatal("可信认证归属未生效或修改了调用方字段")
}
}