116 lines
5.0 KiB
Go
116 lines
5.0 KiB
Go
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("可信认证归属未生效或修改了调用方字段")
|
||
}
|
||
}
|