| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- package service
- import (
- "crypto/x509"
- "encoding/pem"
- "testing"
- "github.com/go-playground/validator/v10"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- )
- func TestNormalizeKeepsMtls(t *testing.T) {
- s := &NodeService{}
- cases := []struct {
- name string
- in model.Node
- wantMode string
- wantErr bool
- }{
- {"mtls over https preserved", model.Node{Name: "n", Address: "node.example.com", Port: 2053, Scheme: "https", TlsVerifyMode: "mtls"}, "mtls", false},
- {"mtls over http rejected", model.Node{Name: "n", Address: "node.example.com", Port: 2053, Scheme: "http", TlsVerifyMode: "mtls"}, "", true},
- {"unknown mode clamped to verify", model.Node{Name: "n", Address: "node.example.com", Port: 2053, Scheme: "https", TlsVerifyMode: "bogus"}, "verify", false},
- }
- for _, c := range cases {
- t.Run(c.name, func(t *testing.T) {
- n := c.in
- err := s.normalize(&n)
- if c.wantErr {
- if err == nil {
- t.Fatal("expected an error")
- }
- return
- }
- if err != nil {
- t.Fatalf("normalize: %v", err)
- }
- if n.TlsVerifyMode != c.wantMode {
- t.Fatalf("TlsVerifyMode = %q, want %q", n.TlsVerifyMode, c.wantMode)
- }
- })
- }
- }
- func TestNodeTlsVerifyModeValidatorAcceptsMtls(t *testing.T) {
- v := validator.New(validator.WithRequiredStructEnabled())
- base := model.Node{Name: "n", Address: "node.example.com", Port: 2053, Scheme: "https", ApiToken: "t"}
- for _, m := range []string{"verify", "skip", "pin", "mtls"} {
- n := base
- n.TlsVerifyMode = m
- if err := v.Struct(n); err != nil {
- t.Fatalf("validator rejected valid TlsVerifyMode %q: %v", m, err)
- }
- }
- bad := base
- bad.TlsVerifyMode = "bogus"
- if err := v.Struct(bad); err == nil {
- t.Fatal("validator must reject an unknown TlsVerifyMode")
- }
- }
- func TestNodeMtlsCaCert(t *testing.T) {
- _ = setupSettingMtlsDB(t)
- got, err := (&NodeService{}).NodeMtlsCaCert()
- if err != nil {
- t.Fatalf("NodeMtlsCaCert: %v", err)
- }
- block, _ := pem.Decode([]byte(got))
- if block == nil || block.Type != "CERTIFICATE" {
- t.Fatalf("NodeMtlsCaCert must return a CERTIFICATE PEM, got %q", got)
- }
- cert, err := x509.ParseCertificate(block.Bytes)
- if err != nil {
- t.Fatalf("parse returned cert: %v", err)
- }
- if !cert.IsCA {
- t.Fatal("NodeMtlsCaCert must return the CA certificate (IsCA)")
- }
- }
- func TestSetNodeMtlsTrustCA(t *testing.T) {
- _ = setupSettingMtlsDB(t)
- ns := &NodeService{}
- settings := SettingService{}
- ca, err := settings.EnsureNodeMtlsCA()
- if err != nil {
- t.Fatalf("EnsureNodeMtlsCA: %v", err)
- }
- if err := ns.SetNodeMtlsTrustCA(string(ca.CertPEM)); err != nil {
- t.Fatalf("SetNodeMtlsTrustCA(valid): %v", err)
- }
- pool, err := settings.NodeMtlsClientCAPool()
- if err != nil || pool == nil {
- t.Fatalf("valid trust CA must persist + build a pool: pool=%v err=%v", pool, err)
- }
- if err := ns.SetNodeMtlsTrustCA("not a certificate"); err == nil {
- t.Fatal("invalid PEM must be rejected (fail closed)")
- }
- if err := ns.SetNodeMtlsTrustCA(""); err != nil {
- t.Fatalf("clearing the trust CA must be allowed: %v", err)
- }
- pool, _ = settings.NodeMtlsClientCAPool()
- if pool != nil {
- t.Fatal("cleared trust CA must yield a nil pool (mTLS off)")
- }
- }
|