mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-17 20:02:23 +03:00
* fix(panel): accept 2FA codes from adjacent TOTP windows
CheckUser compared only gotp.Now(), so a code submitted at the end of
its 30s window (or with slight client/server clock drift) failed with
'invalid 2fa code', while the immediate retry in the next window
succeeded. Accept current +/-1 window, the standard TOTP skew
tolerance.
Fixes MHSanaei/3x-ui#6535
* fix(panel): share TOTP skew tolerance with VerifyTwoFactorCode
Move the +/-1 window helper to internal/util/totp so both 2FA
acceptance points use it: login (CheckUser) and disable/rebind plus
username/password changes (VerifyTwoFactorCode). Also shrink comments
to the 2-line house rule and anchor the unit test mid-window to avoid
a step-boundary flake.
Addresses review on #6546 (MEDIUM + 2 LOWs).
* fix(sub): send stable X-HWID on external subscription fetch
A Master panel fetching a donor subscription sent no X-HWID, so an
HWID-limited donor rejected it with 404. Identify this panel with a
stable per-installation id (persisted in settings), occupying exactly
one donor device slot.
Fixes MHSanaei/3x-ui#6559
* fix(sub): address review on external X-HWID
- Serialize first-time id creation with a mutex so concurrent
first fetches cannot mint two UUIDs.
- Fix goimports grouping for the new third-party import.
- Add externalSubSendHwid opt-out (default send); document it.
- Cover header send/omit with httptest in TestFetchSendsStableHwid.
* fix(sub): drop the SQL-only X-HWID opt-out
The externalSubSendHwid opt-out added in 227ed818 had no settings
field, CLI flag or docs, so an operator could only reach it by editing
the settings table by hand, while every cache-miss fetch paid a query
for it. CLAUDE.md rules out config knobs on a one-header fix.
Also drop the test assertions that only restated the 3x-ui-server-
prefix constant; TestFetchSendsStableHwid still goes red without the
header.
---------
Co-authored-by: sdhfsl <sdhfsl@users.noreply.github.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
68 lines
1.8 KiB
Go
68 lines
1.8 KiB
Go
package sub
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
)
|
|
|
|
// #6559: the Master panel must send a stable X-HWID when fetching external
|
|
// subscriptions, otherwise an HWID-limited donor answers 404.
|
|
func TestServerHwidStableAcrossCalls(t *testing.T) {
|
|
if err := database.InitDB(filepath.Join(t.TempDir(), "x-ui.db")); err != nil {
|
|
t.Fatalf("InitDB: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = database.CloseDB() })
|
|
|
|
first := serverHwid()
|
|
if first == "" {
|
|
t.Fatal("serverHwid returned empty")
|
|
}
|
|
|
|
second := serverHwid()
|
|
if second != first {
|
|
t.Fatalf("hwid not stable: %q vs %q", first, second)
|
|
}
|
|
|
|
var row model.Setting
|
|
if err := database.GetDB().Where("key = ?", serverHwidKey).First(&row).Error; err != nil {
|
|
t.Fatalf("hwid not persisted: %v", err)
|
|
}
|
|
if row.Value != first {
|
|
t.Fatalf("persisted hwid %q != returned %q", row.Value, first)
|
|
}
|
|
}
|
|
|
|
// The fetch must carry the stable id so an HWID-limited donor lets it through.
|
|
func TestFetchSendsStableHwid(t *testing.T) {
|
|
if err := database.InitDB(filepath.Join(t.TempDir(), "x-ui.db")); err != nil {
|
|
t.Fatalf("InitDB: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = database.CloseDB() })
|
|
|
|
var gotHwid string
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
gotHwid = r.Header.Get("X-HWID")
|
|
_, _ = w.Write([]byte("vless://uuid@host:443?security=none#x"))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
res := fetchSubscriptionLinks(srv.URL)
|
|
if res.err != nil {
|
|
t.Fatalf("fetch: %v", res.err)
|
|
}
|
|
if len(res.links) != 1 {
|
|
t.Fatalf("links = %v", res.links)
|
|
}
|
|
if gotHwid == "" {
|
|
t.Fatal("X-HWID header missing on fetch")
|
|
}
|
|
if gotHwid != serverHwid() {
|
|
t.Fatalf("sent %q != stable %q", gotHwid, serverHwid())
|
|
}
|
|
}
|