B6 backend: remove traditional node path, node mTLS and remote merge engine; drop restartXrayOnClientDisable (rider 1) and ApiScopeNodeSync (rider 3)

This commit is contained in:
Muse
2026-10-23 19:08:24 +00:00
parent 7ba1841df2
commit bdfcce0e11
82 changed files with 380 additions and 12275 deletions
+1 -94
View File
@@ -2,12 +2,10 @@ package controller
import (
"crypto/tls"
"crypto/x509"
"net/http"
"net/http/cookiejar"
"net/http/httptest"
"path/filepath"
"reflect"
"testing"
"github.com/gin-contrib/sessions"
@@ -110,100 +108,9 @@ func TestCheckAPIAuth_BearerSuccess(t *testing.T) {
}
}
// TestCheckAPIAuth_AcceptsVerifiedClientCert ensures verified mTLS authenticates
// as node-sync rather than bypassing scope checks as admin.
func TestCheckAPIAuth_AcceptsVerifiedClientCert(t *testing.T) {
engine, _ := newAPIAuthTestEngine(t)
req := httptest.NewRequest(http.MethodGet, "/panel/api/server/status", nil)
req.TLS = &tls.ConnectionState{
VerifiedChains: [][]*x509.Certificate{{&x509.Certificate{}}},
}
w := httptest.NewRecorder()
engine.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status = %d, want 200; body=%s", w.Code, w.Body.String())
}
if got := w.Body.String(); got != `{"api_authed":true,"scope":"node-sync"}` {
t.Fatalf("body = %s, want node-sync scope", got)
}
forbidden := httptest.NewRequest(http.MethodPost, "/panel/api/server/updatePanel", nil)
forbidden.TLS = &tls.ConnectionState{
VerifiedChains: [][]*x509.Certificate{{&x509.Certificate{}}},
}
w = httptest.NewRecorder()
engine.ServeHTTP(w, forbidden)
if w.Code != http.StatusForbidden {
t.Fatalf("updatePanel status = %d, want 403; body=%s", w.Code, w.Body.String())
}
}
func TestNodeSyncScopeAllowlistMatchesRemoteInventory(t *testing.T) {
expected := map[string]map[string]struct{}{
"/server/status": {http.MethodGet: {}},
"/inbounds/list": {http.MethodGet: {}},
"/inbounds/add": {http.MethodPost: {}},
"/inbounds/del/:id": {http.MethodPost: {}},
"/inbounds/update/:id": {http.MethodPost: {}},
"/clients/add": {http.MethodPost: {}},
"/clients/del/:email": {http.MethodPost: {}},
"/clients/:email/detach": {http.MethodPost: {}},
"/clients/update/:email": {http.MethodPost: {}},
"/server/getWebCertFiles": {http.MethodGet: {}},
"/server/descendants": {http.MethodGet: {}},
"/clients/resetTraffic/:email": {http.MethodPost: {}},
"/inbounds/resetAllTraffics": {http.MethodPost: {}},
"/inbounds/:id/resetTraffic": {http.MethodPost: {}},
"/clients/onlinesByGuid": {http.MethodPost: {}},
"/clients/onlines": {http.MethodPost: {}},
"/clients/lastOnline": {http.MethodPost: {}},
"/inbounds/pushClientTraffics": {http.MethodPost: {}},
"/server/clientIps": {http.MethodGet: {}, http.MethodPost: {}},
"/clients/clientIpsByGuid": {http.MethodPost: {}},
"/hosts/list": {http.MethodGet: {}},
}
if !reflect.DeepEqual(nodeSyncScopeAllow, expected) {
t.Fatalf("node-sync allowlist drift:\n got: %#v\nwant: %#v", nodeSyncScopeAllow, expected)
}
if _, ok := nodeSyncScopeAllow["/server/updatePanel"]; ok {
t.Fatal("node-sync must not include /server/updatePanel")
}
}
func TestNodeSyncScopeUsesFullPathPatterns(t *testing.T) {
engine, _ := newAPIAuthTestEngine(t)
cases := []struct {
name string
method string
path string
want int
}{
{"detach email parameter", http.MethodPost, "/panel/api/clients/alice@example.com/detach", http.StatusOK},
{"reset inbound id parameter", http.MethodPost, "/panel/api/inbounds/42/resetTraffic", http.StatusOK},
{"client IP by guid endpoint", http.MethodPost, "/panel/api/clients/clientIpsByGuid", http.StatusOK},
{"update panel forbidden", http.MethodPost, "/panel/api/server/updatePanel", http.StatusForbidden},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
req := httptest.NewRequest(tc.method, tc.path, nil)
req.TLS = &tls.ConnectionState{
VerifiedChains: [][]*x509.Certificate{{&x509.Certificate{}}},
}
w := httptest.NewRecorder()
engine.ServeHTTP(w, req)
if w.Code != tc.want {
t.Fatalf("status = %d, want %d; body=%s", w.Code, tc.want, w.Body.String())
}
})
}
}
// TestCheckAPIAuth_EmptyVerifiedChainsFallsThrough asserts a TLS request with no
// verified client chain is NOT treated as authenticated (it falls through to the
// bearer/session paths) — so the cert branch can't accidentally authorize plain
// browser HTTPS.
// bearer/session paths) — a plain browser HTTPS connection must never authorize.
func TestCheckAPIAuth_EmptyVerifiedChainsFallsThrough(t *testing.T) {
engine, _ := newAPIAuthTestEngine(t)