| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- package service
- import (
- "encoding/json"
- "testing"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- )
- const masqueTestStream = `{"network":"masque","security":"tls","masqueSettings":{"path":"/.well-known/masque/ip/*/*/"},
- "tlsSettings":{"alpn":["h3"],"certificates":[{"certificateFile":"/etc/ssl/certs/m.crt","keyFile":"/etc/ssl/private/m.key"}]}}`
- // GetXrayConfig rebuilds every inbound's users from the clients table; a MASQUE user
- // emitted without its password makes xray-core refuse the whole config at startup.
- func TestGetXrayConfig_EmitsMasqueUserPasswords(t *testing.T) {
- setupConflictDB(t)
- in := &model.Inbound{
- Tag: "in-8443-masque", Enable: true, Listen: "127.0.0.1", Port: 8443, Protocol: model.MASQUE,
- Settings: `{"clients":[{"email":"[email protected]","password":"masque-pass-ivy","enable":true}],"address":["10.14.0.1/24"]}`,
- StreamSettings: masqueTestStream,
- }
- if _, _, err := (&InboundService{}).AddInbound(in); err != nil {
- t.Fatalf("AddInbound: %v", err)
- }
- cfg, err := (&XrayService{}).GetXrayConfig()
- if err != nil {
- t.Fatalf("GetXrayConfig: %v", err)
- }
- for i := range cfg.InboundConfigs {
- if cfg.InboundConfigs[i].Tag != "in-8443-masque" {
- continue
- }
- var settings struct {
- Clients []map[string]any `json:"clients"`
- }
- if err := json.Unmarshal(cfg.InboundConfigs[i].Settings, &settings); err != nil {
- t.Fatalf("decode emitted settings: %v", err)
- }
- if len(settings.Clients) != 1 || settings.Clients[0]["pass"] != "masque-pass-ivy" {
- t.Fatalf("emitted clients = %v, want one with pass masque-pass-ivy", settings.Clients)
- }
- raw, err := json.Marshal(cfg.InboundConfigs[i])
- if err != nil {
- t.Fatalf("marshal emitted inbound: %v", err)
- }
- var emitted map[string]any
- if err := json.Unmarshal(raw, &emitted); err != nil {
- t.Fatalf("decode emitted inbound: %v", err)
- }
- if stream, _ := emitted["streamSettings"].(map[string]any); stream["network"] != "masque" {
- t.Fatalf("emitted streamSettings = %v, want the stored masque transport", emitted["streamSettings"])
- }
- assertXrayAccepts(t, "the emitted MASQUE inbound", buildGoldenInbound(t, emitted))
- return
- }
- t.Fatal("inbound in-8443-masque not found in the generated config")
- }
- // A client added to a MASQUE inbound without a password could never authenticate,
- // and xray-core refuses the whole inbound over one empty pass.
- func TestFillProtocolDefaults_MintsMasquePassword(t *testing.T) {
- client := model.Client{Email: "[email protected]"}
- if err := (&ClientService{}).fillProtocolDefaults(&client, &model.Inbound{Protocol: model.MASQUE}); err != nil {
- t.Fatalf("fillProtocolDefaults: %v", err)
- }
- if len(client.Password) != 32 {
- t.Fatalf("Password = %q, want a minted 32-character password", client.Password)
- }
- kept := model.Client{Email: "[email protected]", Password: "chosen"}
- if err := (&ClientService{}).fillProtocolDefaults(&kept, &model.Inbound{Protocol: model.MASQUE}); err != nil {
- t.Fatalf("fillProtocolDefaults: %v", err)
- }
- if kept.Password != "chosen" {
- t.Fatalf("Password = %q, want the chosen password kept", kept.Password)
- }
- }
- // Editing a MASQUE client must key on its password like trojan; falling back to the
- // empty UUID refused every MASQUE client edit with "empty client ID".
- func TestUpdateInboundClient_MasquePasswordChange(t *testing.T) {
- setupBulkDB(t)
- svc := &ClientService{}
- source := []model.Client{{Email: "ivy@x", Password: "pw-old", SubID: "sub-ivy", Enable: true}}
- ib := mkInbound(t, 22101, model.MASQUE, clientsSettings(t, source))
- if err := svc.SyncInbound(nil, ib.Id, source); err != nil {
- t.Fatalf("seed linkage: %v", err)
- }
- updated := []model.Client{{Email: "ivy@x", Password: "pw-new", SubID: "sub-ivy", Enable: true}}
- if _, err := svc.UpdateInboundClient(&InboundService{}, &model.Inbound{
- Id: ib.Id,
- Settings: clientsSettings(t, updated),
- }, "ivy@x"); err != nil {
- t.Fatalf("UpdateInboundClient: %v", err)
- }
- if rec := lookupClientRecord(t, "ivy@x"); rec.Password != "pw-new" {
- t.Fatalf("stored password = %q, want pw-new", rec.Password)
- }
- }
|