mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 11:15:03 +03:00
2e14a2a51a
A failed login keeps its username in the in-memory log buffer, the limiter table and the notifier queue, and the route accepted any body the global cap allowed. The login route now takes at most 16 KiB and answers a username over 256 bytes with the generic failure before any of those see it; the user setters refuse such a name too, so no stored username can be one the login route turns away.
77 lines
2.6 KiB
Go
77 lines
2.6 KiB
Go
package panel
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/dbtest"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/util/crypto"
|
|
)
|
|
|
|
// install.sh replaces the credentials only while this reports true, and before
|
|
// the first start a database holds no user at all.
|
|
func TestHasDefaultCredential(t *testing.T) {
|
|
hash := func(t *testing.T, password string) string {
|
|
t.Helper()
|
|
h, err := crypto.HashPasswordAsBcrypt(password)
|
|
if err != nil {
|
|
t.Fatalf("hash: %v", err)
|
|
}
|
|
return h
|
|
}
|
|
cases := []struct {
|
|
name string
|
|
user *model.User
|
|
want bool
|
|
}{
|
|
{name: "no user before the first start", user: nil, want: true},
|
|
{name: "legacy admin/admin", user: &model.User{Username: "admin", Password: hash(t, "admin")}, want: true},
|
|
{name: "chosen credentials", user: &model.User{Username: "admin", Password: hash(t, "s3cret-Pass")}, want: false},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
dbtest.InitDB(t, filepath.Join(t.TempDir(), "x-ui.db"))
|
|
if err := database.GetDB().Where("1 = 1").Delete(&model.User{}).Error; err != nil {
|
|
t.Fatalf("clear users: %v", err)
|
|
}
|
|
if tc.user != nil {
|
|
if err := database.GetDB().Create(tc.user).Error; err != nil {
|
|
t.Fatalf("create user: %v", err)
|
|
}
|
|
}
|
|
got, err := (&UserService{}).HasDefaultCredential()
|
|
if err != nil || got != tc.want {
|
|
t.Fatalf("HasDefaultCredential() = %v, %v; want %v", got, err, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// The login route refuses names over MaxUsernameLength unread, so storing one would lock
|
|
// the admin out of their own panel.
|
|
func TestUserSettersRefuseNamesTheLoginRouteRefuses(t *testing.T) {
|
|
dbtest.InitDB(t, filepath.Join(t.TempDir(), "x-ui.db"))
|
|
svc := &UserService{}
|
|
if err := svc.UpdateFirstUser("admin", "s3cret-Pass"); err != nil {
|
|
t.Fatalf("UpdateFirstUser: %v", err)
|
|
}
|
|
var user model.User
|
|
if err := database.GetDB().First(&user).Error; err != nil {
|
|
t.Fatalf("load user: %v", err)
|
|
}
|
|
long := strings.Repeat("a", MaxUsernameLength+1)
|
|
if err := svc.UpdateFirstUser(long, "s3cret-Pass"); err == nil {
|
|
t.Fatal("UpdateFirstUser stored a name the login route refuses")
|
|
}
|
|
if err := svc.UpdateUser(user.Id, long, "s3cret-Pass"); err == nil {
|
|
t.Fatal("UpdateUser stored a name the login route refuses")
|
|
}
|
|
var stored model.User
|
|
if err := database.GetDB().First(&stored, user.Id).Error; err != nil || stored.Username != "admin" {
|
|
t.Fatalf("stored username = %q (%v), want it unchanged", stored.Username, err)
|
|
}
|
|
}
|