mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-29 09:04:19 +00:00
2b10808fbd
* fix(settings): require server-side 2fa for sensitive changes * fix(lint): group third-party imports separately from local (goimports) golangci-lint goimports flagged setting.go and setting_security_test.go because xlzd/gotp and gorm.io/gorm were mixed into the github.com/mhsanaei/3x-ui local-prefix group. Move them into the third-party group so the local imports stand alone.
124 lines
3.5 KiB
Go
124 lines
3.5 KiB
Go
package service
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/xlzd/gotp"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
)
|
|
|
|
func setupSettingTestDB(t *testing.T) {
|
|
t.Helper()
|
|
if err := database.InitDB(filepath.Join(t.TempDir(), "x-ui.db")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if err := database.CloseDB(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestGetAllSettingViewRedactsSecrets(t *testing.T) {
|
|
setupSettingTestDB(t)
|
|
s := &SettingService{}
|
|
if err := s.saveSetting("tgBotToken", "telegram-secret"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.saveSetting("twoFactorToken", "totp-secret"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.saveSetting("ldapPassword", "ldap-secret"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.saveSetting("smtpPassword", "smtp-secret"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := database.GetDB().Create(&model.ApiToken{Name: "test", Token: "api-secret", Enabled: true}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
view, err := s.GetAllSettingView()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if view.TgBotToken != "" || view.TwoFactorToken != "" || view.LdapPassword != "" || view.SmtpPassword != "" {
|
|
t.Fatalf("settings view leaked secrets: %#v", view)
|
|
}
|
|
if !view.HasTgBotToken || !view.HasTwoFactorToken || !view.HasLdapPassword || !view.HasApiToken || !view.HasSmtpPassword {
|
|
t.Fatalf("settings view did not report configured secret flags: %#v", view)
|
|
}
|
|
}
|
|
|
|
func TestUpdateAllSettingPreservesRedactedSecrets(t *testing.T) {
|
|
setupSettingTestDB(t)
|
|
s := &SettingService{}
|
|
if err := s.saveSetting("tgBotToken", "telegram-secret"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.saveSetting("ldapPassword", "ldap-secret"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.saveSetting("twoFactorEnable", "true"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.saveSetting("twoFactorToken", "totp-secret"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.saveSetting("smtpPassword", "smtp-secret"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
view, err := s.GetAllSettingView()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
settings := &view.AllSetting
|
|
if err := s.UpdateAllSetting(settings); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, _ := s.GetTgBotToken(); got != "telegram-secret" {
|
|
t.Fatalf("tg token = %q, want preserved secret", got)
|
|
}
|
|
if got, _ := s.GetLdapPassword(); got != "ldap-secret" {
|
|
t.Fatalf("ldap password = %q, want preserved secret", got)
|
|
}
|
|
if got, _ := s.GetTwoFactorToken(); got != "totp-secret" {
|
|
t.Fatalf("2fa token = %q, want preserved secret", got)
|
|
}
|
|
if got, _ := s.GetSmtpPassword(); got != "smtp-secret" {
|
|
t.Fatalf("smtp password = %q, want preserved secret", got)
|
|
}
|
|
}
|
|
|
|
func TestSanitizePublicHTTPURLBlocksPrivateAddressUnlessAllowed(t *testing.T) {
|
|
if _, err := SanitizePublicHTTPURL("http://127.0.0.1:8080/hook", false); err == nil {
|
|
t.Fatal("expected localhost URL to be blocked")
|
|
}
|
|
if got, err := SanitizePublicHTTPURL("http://127.0.0.1:8080/hook", true); err != nil || got != "http://127.0.0.1:8080/hook" {
|
|
t.Fatalf("allowPrivate result = %q, %v", got, err)
|
|
}
|
|
}
|
|
|
|
func TestVerifyTwoFactorCode(t *testing.T) {
|
|
setupSettingTestDB(t)
|
|
s := &SettingService{}
|
|
if err := s.saveSetting("twoFactorEnable", "true"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
const token = "JBSWY3DPEHPK3PXP"
|
|
if err := s.saveSetting("twoFactorToken", token); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := s.VerifyTwoFactorCode(gotp.NewDefaultTOTP(token).Now()); err != nil {
|
|
t.Fatalf("valid code rejected: %v", err)
|
|
}
|
|
if err := s.VerifyTwoFactorCode("000000"); err == nil {
|
|
t.Fatal("invalid code accepted")
|
|
}
|
|
}
|