client_wireguard_crud_test.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. package service
  2. import (
  3. "testing"
  4. "github.com/mhsanaei/3x-ui/v3/internal/database"
  5. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  6. )
  7. func wgServerSettings() string {
  8. return `{"secretKey":"` + wgTestSecretKey() + `","mtu":1420,"clients":[]}`
  9. }
  10. func lookupClientRecord(t *testing.T, email string) model.ClientRecord {
  11. t.Helper()
  12. var rec model.ClientRecord
  13. if err := database.GetDB().Where("email = ?", email).First(&rec).Error; err != nil {
  14. t.Fatalf("lookup client %q: %v", email, err)
  15. }
  16. return rec
  17. }
  18. func TestWireGuardClientAddUpdateDeleteRoundTrip(t *testing.T) {
  19. setupBulkDB(t)
  20. svc := &ClientService{}
  21. inboundSvc := &InboundService{}
  22. ib := mkInbound(t, 51900, model.WireGuard, wgServerSettings())
  23. add := &model.Inbound{Id: ib.Id, Protocol: model.WireGuard, Settings: clientsSettings(t, []model.Client{
  24. {Email: "alice@wg", Enable: true},
  25. })}
  26. if _, err := svc.AddInboundClient(inboundSvc, add); err != nil {
  27. t.Fatalf("AddInboundClient: %v", err)
  28. }
  29. list, err := svc.ListForInbound(nil, ib.Id)
  30. if err != nil {
  31. t.Fatalf("ListForInbound: %v", err)
  32. }
  33. if len(list) != 1 {
  34. t.Fatalf("expected 1 attached client, got %d", len(list))
  35. }
  36. created := list[0]
  37. if created.PrivateKey == "" || created.PublicKey == "" {
  38. t.Fatalf("keys not generated/persisted: %+v", created)
  39. }
  40. if len(created.AllowedIPs) == 0 {
  41. t.Fatalf("allowedIPs not allocated: %+v", created)
  42. }
  43. rec := lookupClientRecord(t, "alice@wg")
  44. if rec.PrivateKey == "" || rec.AllowedIPs == "" {
  45. t.Fatalf("client record missing wg columns: %+v", rec)
  46. }
  47. update := &model.Inbound{Id: ib.Id, Protocol: model.WireGuard, Settings: clientsSettings(t, []model.Client{
  48. {Email: "alice@wg", Enable: true, Comment: "renamed laptop"},
  49. })}
  50. if _, err := svc.UpdateInboundClient(inboundSvc, update, "alice@wg"); err != nil {
  51. t.Fatalf("UpdateInboundClient: %v", err)
  52. }
  53. afterUpdate := lookupClientRecord(t, "alice@wg")
  54. if afterUpdate.PrivateKey != created.PrivateKey {
  55. t.Fatalf("private key rotated on metadata edit: was %q now %q", created.PrivateKey, afterUpdate.PrivateKey)
  56. }
  57. if afterUpdate.PublicKey != created.PublicKey {
  58. t.Fatalf("public key rotated on metadata edit: was %q now %q", created.PublicKey, afterUpdate.PublicKey)
  59. }
  60. if afterUpdate.Comment != "renamed laptop" {
  61. t.Fatalf("comment not updated: %q", afterUpdate.Comment)
  62. }
  63. listAfter, err := svc.ListForInbound(nil, ib.Id)
  64. if err != nil {
  65. t.Fatalf("ListForInbound after update: %v", err)
  66. }
  67. if len(listAfter) != 1 || len(listAfter[0].AllowedIPs) == 0 {
  68. t.Fatalf("settings lost wg fields after metadata edit: %+v", listAfter)
  69. }
  70. if _, err := svc.DelInboundClientByEmail(inboundSvc, ib.Id, "alice@wg", false); err != nil {
  71. t.Fatalf("DelInboundClientByEmail: %v", err)
  72. }
  73. final, err := svc.ListForInbound(nil, ib.Id)
  74. if err != nil {
  75. t.Fatalf("ListForInbound after delete: %v", err)
  76. }
  77. if len(final) != 0 {
  78. t.Fatalf("client not detached after delete: %+v", final)
  79. }
  80. }
  81. func TestWireGuardClientAddToInboundWithoutClientsKey(t *testing.T) {
  82. setupBulkDB(t)
  83. svc := &ClientService{}
  84. inboundSvc := &InboundService{}
  85. ib := mkInbound(t, 51902, model.WireGuard, `{"secretKey":"`+wgTestSecretKey()+`","mtu":1420,"peers":[]}`)
  86. add := &model.Inbound{Id: ib.Id, Protocol: model.WireGuard, Settings: clientsSettings(t, []model.Client{
  87. {Email: "first@wg", Enable: true},
  88. })}
  89. if _, err := svc.AddInboundClient(inboundSvc, add); err != nil {
  90. t.Fatalf("AddInboundClient onto clients-less wireguard inbound: %v", err)
  91. }
  92. list, err := svc.ListForInbound(nil, ib.Id)
  93. if err != nil {
  94. t.Fatalf("ListForInbound: %v", err)
  95. }
  96. if len(list) != 1 || list[0].PrivateKey == "" || len(list[0].AllowedIPs) == 0 {
  97. t.Fatalf("client not added with generated keys/address: %+v", list)
  98. }
  99. }
  100. func TestWireGuardClientAllocatesUniqueIPsAcrossTwoAdds(t *testing.T) {
  101. setupBulkDB(t)
  102. svc := &ClientService{}
  103. inboundSvc := &InboundService{}
  104. ib := mkInbound(t, 51901, model.WireGuard, wgServerSettings())
  105. for _, email := range []string{"one@wg", "two@wg"} {
  106. add := &model.Inbound{Id: ib.Id, Protocol: model.WireGuard, Settings: clientsSettings(t, []model.Client{
  107. {Email: email, Enable: true},
  108. })}
  109. if _, err := svc.AddInboundClient(inboundSvc, add); err != nil {
  110. t.Fatalf("AddInboundClient(%s): %v", email, err)
  111. }
  112. }
  113. list, err := svc.ListForInbound(nil, ib.Id)
  114. if err != nil {
  115. t.Fatalf("ListForInbound: %v", err)
  116. }
  117. if len(list) != 2 {
  118. t.Fatalf("expected 2 clients, got %d", len(list))
  119. }
  120. if list[0].AllowedIPs[0] == list[1].AllowedIPs[0] {
  121. t.Fatalf("two adds collided on address %q", list[0].AllowedIPs[0])
  122. }
  123. }