Smail/backend/controllers/config_backup_access_test.go
smail f60d3b0880
Some checks are pending
Security Checks / security (push) Waiting to run
整理前后端与测试构建目录并完善配置和安全引导
统一 frontend、backend、test、test-log 和 out 目录,更新构建、部署及测试入口;纳入配置管理兼容与首次安全引导修复;排除本机配置、密钥和测试产物。
2026-09-05 19:19:52 +08:00

77 lines
2.7 KiB
Go

package controllers
import (
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"git.pcux.cn/xuans/Smail/config"
"git.pcux.cn/xuans/Smail/services/access"
appcontext "git.pcux.cn/xuans/Smail/utils/context"
)
func TestConfigBackupReadDoesNotRequireRecentAuth(t *testing.T) {
root := t.TempDir()
if err := os.MkdirAll(filepath.Join(root, "config"), 0700); err != nil {
t.Fatal(err)
}
legacy := []byte(`{"domain":"mail.example.com","webDomain":"mail.example.com","dbType":"sqlite","dbDSN":"./config/test.db","isInit":true,"httpPort":80,"httpsPort":443,"httpsEnabled":1}`)
if err := os.WriteFile(filepath.Join(root, "config", "config.json"), legacy, 0600); err != nil {
t.Fatal(err)
}
validation := config.RuntimeValidationOptions{HasSuperAdmin: true, Production: true}
if _, err := config.MigrateLegacyConfig(root, validation); err != nil {
t.Fatal(err)
}
manager, err := config.NewConfigManager(root, config.ConfigLoaderOptions{Validation: validation})
if err != nil {
t.Fatal(err)
}
previous := config.CurrentManager()
config.SetRuntimeManager(manager)
t.Cleanup(func() { config.SetRuntimeManager(previous) })
for _, role := range []access.Role{access.RoleSuperAdmin, access.RoleAdmin, access.RoleUser} {
t.Run(string(role), func(t *testing.T) {
ctx := &appcontext.Context{UserID: 1, Role: role, RecentAuth: false}
recorder := httptest.NewRecorder()
ListConfigBackups(ctx, recorder, httptest.NewRequest(http.MethodGet, "/api/v1/config/backups", nil))
if role == access.RoleUser {
if recorder.Code != http.StatusForbidden {
t.Fatalf("普通用户不应读取配置备份:%d", recorder.Code)
}
return
}
var payload struct {
ErrorNo int `json:"errorNo"`
}
if err := json.Unmarshal(recorder.Body.Bytes(), &payload); err != nil {
t.Fatal(err)
}
if recorder.Code != http.StatusOK || payload.ErrorNo != 0 {
t.Fatalf("管理员读取脱敏备份不应要求近期认证:%d", recorder.Code)
}
})
}
// 列表读取放宽不能改变实际回滚链路的近期认证要求。
for name, handler := range map[string]func(*appcontext.Context, http.ResponseWriter, *http.Request){
"preview": PreviewConfigRollback,
"ticket": IssueConfigRollbackTicket,
"execute": RollbackConfig,
} {
t.Run(name, func(t *testing.T) {
recorder := httptest.NewRecorder()
handler(&appcontext.Context{UserID: 1, Role: access.RoleSuperAdmin}, recorder,
httptest.NewRequest(http.MethodPost, "/api/v1/config/rollback", strings.NewReader(`{"targetGeneration":1}`)))
if recorder.Code != http.StatusForbidden || !strings.Contains(recorder.Body.String(), "RECENT_AUTH_REQUIRED") {
t.Fatalf("回滚仍应要求近期认证:%d", recorder.Code)
}
})
}
}