mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
304 lines
9.1 KiB
Go
304 lines
9.1 KiB
Go
package model
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestReadConfig(t *testing.T) {
|
|
t.Run("ReadEmptyConfig", func(t *testing.T) {
|
|
file := newTempConfig(t, "")
|
|
c := &Config{}
|
|
|
|
if err := c.Read(file, nil); err != nil {
|
|
t.Fatalf("read empty config failed: %v", err)
|
|
}
|
|
|
|
testFields := []struct {
|
|
Name string
|
|
Value any
|
|
Cond bool
|
|
}{
|
|
{"jwt_secret_key", c.JWTSecretKey, c.JWTSecretKey != ""},
|
|
{"user_template", c.UserTemplate, c.UserTemplate == "user-dist"},
|
|
{"admin_template", c.AdminTemplate, c.AdminTemplate == "admin-dist"},
|
|
{"agent_secret_key", c.AgentSecretKey, c.AgentSecretKey != ""},
|
|
}
|
|
|
|
for _, field := range testFields {
|
|
if !field.Cond {
|
|
t.Fatalf("%s did not passed check, value: %v", field.Name, field.Value)
|
|
}
|
|
}
|
|
|
|
os.Remove(file)
|
|
})
|
|
|
|
t.Run("ReadFile", func(t *testing.T) {
|
|
const testCfg = `
|
|
jwt_secret_key: test
|
|
user_template: um
|
|
admin_template: am
|
|
agent_secret_key: none
|
|
site_name: lowkick
|
|
telegram_bot_token: 8155880635:AAH_test
|
|
telegram_admin_chat_id: "12345678"
|
|
web_real_ip_header: nz-realip
|
|
agent_real_ip_header: nz-realip
|
|
domain_expiry_notification_days: 100, 30, 7, 1, 0
|
|
server_expiry_notification_days: 30, 7, 3, 1, 0
|
|
smtp_server: us1.workspace.org:465
|
|
smtp_user: test@loohui.com
|
|
smtp_password: password123
|
|
admin_email: admin@loohui.com
|
|
`
|
|
|
|
var testFrontendTemplates = []FrontendTemplate{
|
|
{Path: "um"},
|
|
{Path: "am", IsAdmin: true},
|
|
}
|
|
file := newTempConfig(t, testCfg)
|
|
c := &Config{}
|
|
|
|
if err := c.Read(file, testFrontendTemplates); err != nil {
|
|
t.Fatalf("read config failed: %v", err)
|
|
}
|
|
|
|
testFields := []struct {
|
|
Name string
|
|
Value any
|
|
Cond bool
|
|
}{
|
|
{"jwt_secret_key", c.JWTSecretKey, c.JWTSecretKey == "test"},
|
|
{"user_template", c.UserTemplate, c.UserTemplate == "um"},
|
|
{"admin_template", c.AdminTemplate, c.AdminTemplate == "am"},
|
|
{"agent_secret_key", c.AgentSecretKey, c.AgentSecretKey == "none"},
|
|
{"site_name", c.SiteName, c.SiteName == "lowkick"},
|
|
{"telegram_bot_token", c.TelegramBotToken, c.TelegramBotToken == "8155880635:AAH_test"},
|
|
{"telegram_admin_chat_id", c.TelegramAdminChatID, c.TelegramAdminChatID == "12345678"},
|
|
{"web_real_ip_header", c.WebRealIPHeader, c.WebRealIPHeader == "nz-realip"},
|
|
{"agent_real_ip_header", c.AgentRealIPHeader, c.AgentRealIPHeader == "nz-realip"},
|
|
{"domain_expiry_notification_days", c.DomainExpiryNotificationDays, c.DomainExpiryNotificationDays == "100, 30, 7, 1, 0"},
|
|
{"server_expiry_notification_days", c.ServerExpiryNotificationDays, c.ServerExpiryNotificationDays == "30, 7, 3, 1, 0"},
|
|
{"smtp_server", c.SMTPServer, c.SMTPServer == "us1.workspace.org:465"},
|
|
{"smtp_user", c.SMTPUser, c.SMTPUser == "test@loohui.com"},
|
|
{"smtp_password", c.SMTPPassword, c.SMTPPassword == "password123"},
|
|
{"admin_email", c.AdminEmail, c.AdminEmail == "admin@loohui.com"},
|
|
}
|
|
|
|
for _, field := range testFields {
|
|
if !field.Cond {
|
|
t.Fatalf("%s did not passed check, value: %v", field.Name, field.Value)
|
|
}
|
|
}
|
|
|
|
os.Remove(file)
|
|
})
|
|
|
|
t.Run("ReadEnv", func(t *testing.T) {
|
|
os.Setenv("NZ_JWTSECRETKEY", "test")
|
|
os.Setenv("NZ_USERTEMPLATE", "um")
|
|
os.Setenv("NZ_ADMINTEMPLATE", "am")
|
|
os.Setenv("NZ_AGENTSECRETKEY", "none")
|
|
os.Setenv("NZ_HTTPS_LISTENPORT", "9876")
|
|
|
|
var testFrontendTemplates = []FrontendTemplate{
|
|
{Path: "um"},
|
|
{Path: "am", IsAdmin: true},
|
|
}
|
|
file := newTempConfig(t, "")
|
|
c := &Config{}
|
|
|
|
if err := c.Read(file, testFrontendTemplates); err != nil {
|
|
t.Fatalf("read empty config failed: %v", err)
|
|
}
|
|
|
|
testFields := []struct {
|
|
Name string
|
|
Value any
|
|
Cond bool
|
|
}{
|
|
{"jwt_secret_key", c.JWTSecretKey, c.JWTSecretKey == "test"},
|
|
{"user_template", c.UserTemplate, c.UserTemplate == "um"},
|
|
{"admin_template", c.AdminTemplate, c.AdminTemplate == "am"},
|
|
{"agent_secret_key", c.AgentSecretKey, c.AgentSecretKey == "none"},
|
|
{"https.listenport", c.HTTPS.ListenPort, c.HTTPS.ListenPort == 9876},
|
|
}
|
|
|
|
for _, field := range testFields {
|
|
if !field.Cond {
|
|
t.Fatalf("%s did not passed check, value: %v", field.Name, field.Value)
|
|
}
|
|
}
|
|
|
|
os.Remove(file)
|
|
})
|
|
|
|
t.Run("ReadEnvFile", func(t *testing.T) {
|
|
t.Setenv("NZ_JWTSECRETKEY", "test1")
|
|
t.Setenv("NZ_USERTEMPLATE", "um1")
|
|
t.Setenv("NZ_ADMINTEMPLATE", "am1")
|
|
t.Setenv("NZ_AGENTSECRETKEY", "none1")
|
|
t.Setenv("NZ_SITENAME", "lowkick1")
|
|
|
|
const testCfg = "jwt_secret_key: test\nuser_template: um\nadmin_template: am\nagent_secret_key: none\nsite_name: lowkick"
|
|
|
|
var testFrontendTemplates = []FrontendTemplate{
|
|
{Path: "um"},
|
|
{Path: "am", IsAdmin: true},
|
|
{Path: "um1"},
|
|
{Path: "am1", IsAdmin: true},
|
|
}
|
|
file := newTempConfig(t, testCfg)
|
|
c := &Config{}
|
|
|
|
if err := c.Read(file, testFrontendTemplates); err != nil {
|
|
t.Fatalf("read empty config failed: %v", err)
|
|
}
|
|
|
|
testFields := []struct {
|
|
Name string
|
|
Value any
|
|
Cond bool
|
|
}{
|
|
{"jwt_secret_key", c.JWTSecretKey, c.JWTSecretKey == "test1"},
|
|
{"jwt_secret_from_env", c.jwtSecretFromEnv, c.jwtSecretFromEnv},
|
|
{"user_template", c.UserTemplate, c.UserTemplate == "um1" || c.UserTemplate == "um"},
|
|
{"admin_template", c.AdminTemplate, c.AdminTemplate == "am1" || c.AdminTemplate == "am"},
|
|
{"agent_secret_key", c.AgentSecretKey, c.AgentSecretKey == "none" || c.AgentSecretKey == "none1"},
|
|
{"site_name", c.SiteName, c.SiteName == "lowkick" || c.SiteName == "lowkick1"},
|
|
}
|
|
|
|
for _, field := range testFields {
|
|
if !field.Cond {
|
|
t.Fatalf("%s did not passed check, value: %v", field.Name, field.Value)
|
|
}
|
|
}
|
|
|
|
os.Remove(file)
|
|
})
|
|
}
|
|
|
|
func TestRotateJWTSecretKeyIfNeeded(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
initialMarker string
|
|
currentVersion string
|
|
wantRotated bool
|
|
wantStoredVersion string
|
|
wantSecretChanged bool
|
|
wantSavedConfigKey bool
|
|
}{
|
|
{
|
|
name: "empty marker rotates leaked secret",
|
|
currentVersion: "v2.0.13",
|
|
wantRotated: true,
|
|
wantStoredVersion: "v2.0.13",
|
|
wantSecretChanged: true,
|
|
wantSavedConfigKey: true,
|
|
},
|
|
{
|
|
name: "old marker rotates leaked secret",
|
|
initialMarker: "v2.0.12",
|
|
currentVersion: "v2.0.14",
|
|
wantRotated: true,
|
|
wantStoredVersion: "v2.0.14",
|
|
wantSecretChanged: true,
|
|
wantSavedConfigKey: true,
|
|
},
|
|
{
|
|
name: "threshold marker keeps secret and advances marker",
|
|
initialMarker: "v2.0.13",
|
|
currentVersion: "v2.0.14",
|
|
wantStoredVersion: "v2.0.14",
|
|
wantSavedConfigKey: true,
|
|
},
|
|
{
|
|
name: "current marker keeps secret",
|
|
initialMarker: "v2.0.14",
|
|
currentVersion: "v2.0.14",
|
|
wantStoredVersion: "v2.0.14",
|
|
},
|
|
{
|
|
name: "debug version skips rotation and marker update",
|
|
currentVersion: "debug",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
file := newTempConfig(t, "")
|
|
t.Cleanup(func() { os.Remove(file) })
|
|
|
|
c := &Config{
|
|
JWTSecretKey: "leaked-secret",
|
|
JWTSecretKeyLastRotatedVersion: tt.initialMarker,
|
|
filePath: file,
|
|
}
|
|
|
|
rotated, err := c.RotateJWTSecretKeyIfNeeded(tt.currentVersion)
|
|
if err != nil {
|
|
t.Fatalf("rotate jwt secret key failed: %v", err)
|
|
}
|
|
if rotated != tt.wantRotated {
|
|
t.Fatalf("rotated = %v, want %v", rotated, tt.wantRotated)
|
|
}
|
|
if c.JWTSecretKeyLastRotatedVersion != tt.wantStoredVersion {
|
|
t.Fatalf("jwt secret key marker = %q, want %q", c.JWTSecretKeyLastRotatedVersion, tt.wantStoredVersion)
|
|
}
|
|
secretChanged := c.JWTSecretKey != "leaked-secret"
|
|
if secretChanged != tt.wantSecretChanged {
|
|
t.Fatalf("secret changed = %v, want %v", secretChanged, tt.wantSecretChanged)
|
|
}
|
|
|
|
saved, err := os.ReadFile(file)
|
|
if err != nil {
|
|
t.Fatalf("read saved config: %v", err)
|
|
}
|
|
hasMarker := strings.Contains(string(saved), "jwt_secret_key_last_rotated_version")
|
|
if hasMarker != tt.wantSavedConfigKey {
|
|
t.Fatalf("saved marker present = %v, want %v, config = %s", hasMarker, tt.wantSavedConfigKey, saved)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Mirrors the upstream single-block declaration so iota lines up exactly:
|
|
// ConfigUsePeerIP occupies iota=0 (as a typed string), ConfigCoverAll=1,
|
|
// ConfigCoverIgnoreAll=2. Pins persisted `cover` semantics.
|
|
const (
|
|
originalConfigUsePeerIP = "NZ::Use-Peer-IP"
|
|
originalConfigCoverAll = iota
|
|
originalConfigCoverIgnoreAll
|
|
)
|
|
|
|
func TestConfigCoverConstantValues(t *testing.T) {
|
|
if ConfigUsePeerIP != originalConfigUsePeerIP {
|
|
t.Fatalf("ConfigUsePeerIP = %q, want %q", ConfigUsePeerIP, originalConfigUsePeerIP)
|
|
}
|
|
if ConfigCoverAll != originalConfigCoverAll {
|
|
t.Fatalf("ConfigCoverAll = %d, want original value %d", ConfigCoverAll, originalConfigCoverAll)
|
|
}
|
|
if ConfigCoverIgnoreAll != originalConfigCoverIgnoreAll {
|
|
t.Fatalf("ConfigCoverIgnoreAll = %d, want original value %d", ConfigCoverIgnoreAll, originalConfigCoverIgnoreAll)
|
|
}
|
|
}
|
|
|
|
func newTempConfig(t *testing.T, cfg string) string {
|
|
t.Helper()
|
|
|
|
file, err := os.CreateTemp(os.TempDir(), "nezha-test-config-*.yml")
|
|
if err != nil {
|
|
t.Fatalf("create temp file failed: %v", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
_, err = file.ReadFrom(strings.NewReader(cfg))
|
|
if err != nil {
|
|
t.Fatalf("write to temp file failed: %v", err)
|
|
}
|
|
|
|
return file.Name()
|
|
}
|