Files
opsx-vpn/backend/internal/api/tls_certificate_test.go
T

120 lines
3.4 KiB
Go

package api
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"math/big"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/gin-gonic/gin"
"vpn-platform/backend/internal/service"
"vpn-platform/backend/internal/tlsservice"
)
func pemPairForAPI(t *testing.T, host string) (certPEM, keyPEM string) {
t.Helper()
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
serial, err := rand.Int(rand.Reader, big.NewInt(1<<62))
if err != nil {
t.Fatal(err)
}
tmpl := &x509.Certificate{
SerialNumber: serial,
Subject: pkix.Name{CommonName: host},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(24 * time.Hour),
DNSNames: []string{host},
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
if err != nil {
t.Fatal(err)
}
keyDER, err := x509.MarshalECPrivateKey(key)
if err != nil {
t.Fatal(err)
}
return string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})),
string(pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER}))
}
func TestUploadTLSCertificateAPI(t *testing.T) {
gin.SetMode(gin.TestMode)
dir := t.TempDir()
t.Setenv("VPN_DATA_DIR", dir)
t.Setenv("VPN_TLS_DOMAIN", "")
t.Setenv("VPN_TLS_SCHEME", "")
t.Setenv("VPN_TLS_PORT", "")
t.Setenv("VPN_TLS_MODE", "")
t.Setenv("VPN_TLS_HTTP_ADDR", "127.0.0.1:0")
t.Setenv("VPN_TLS_HTTPS_ADDR", "127.0.0.1:0")
if err := tlsservice.ConfigureMode(dir, "nginx"); err != nil {
t.Fatal(err)
}
if err := tlsservice.ConfigurePublicEntry(dir, "https", "vpn.example.com", 0); err != nil {
t.Fatal(err)
}
if err := tlsservice.Start(http.NotFoundHandler(), dir); err != nil {
t.Fatal(err)
}
_ = service.BaseDir() // ensure env seen
router := gin.New()
s := &Server{}
router.POST("/api/system/tls/certificate", func(c *gin.Context) {
c.Set("role", "admin")
c.Set("username", "admin")
c.Next()
}, s.uploadTLSCertificate)
certPEM, keyPEM := pemPairForAPI(t, "vpn.example.com")
body, _ := json.Marshal(map[string]string{
"certificate_pem": certPEM,
"private_key_pem": keyPEM,
"host": "vpn.example.com",
})
req := httptest.NewRequest(http.MethodPost, "/api/system/tls/certificate", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
resp := httptest.NewRecorder()
router.ServeHTTP(resp, req)
if resp.Code != http.StatusOK {
t.Fatalf("upload HTTP %d: %s", resp.Code, resp.Body.String())
}
var out map[string]any
if err := json.Unmarshal(resp.Body.Bytes(), &out); err != nil {
t.Fatal(err)
}
status, _ := out["status"].(map[string]any)
if status["cert_source"] != "upload" || status["certificate_valid"] != true {
t.Fatalf("status=%v", status)
}
// mismatch
_, otherKey := pemPairForAPI(t, "vpn.example.com")
bad, _ := json.Marshal(map[string]string{
"certificate_pem": certPEM,
"private_key_pem": otherKey,
"host": "vpn.example.com",
})
req = httptest.NewRequest(http.MethodPost, "/api/system/tls/certificate", bytes.NewReader(bad))
req.Header.Set("Content-Type", "application/json")
resp = httptest.NewRecorder()
router.ServeHTTP(resp, req)
if resp.Code != http.StatusBadRequest {
t.Fatalf("mismatch want 400 got %d", resp.Code)
}
}