525 lines
18 KiB
Go
525 lines
18 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/Wikid82/charon/backend/internal/crypto"
|
|
"github.com/Wikid82/charon/backend/internal/models"
|
|
)
|
|
|
|
// newTestEncryptionService creates a real EncryptionService for tests.
|
|
func newTestEncryptionService(t *testing.T) *crypto.EncryptionService {
|
|
t.Helper()
|
|
key := make([]byte, 32)
|
|
for i := range key {
|
|
key[i] = byte(i)
|
|
}
|
|
keyB64 := base64.StdEncoding.EncodeToString(key)
|
|
svc, err := crypto.NewEncryptionService(keyB64)
|
|
require.NoError(t, err)
|
|
return svc
|
|
}
|
|
|
|
func newTestCertServiceWithEnc(t *testing.T, dataDir string, db *gorm.DB) *CertificateService {
|
|
t.Helper()
|
|
encSvc := newTestEncryptionService(t)
|
|
return &CertificateService{
|
|
dataDir: dataDir,
|
|
db: db,
|
|
encSvc: encSvc,
|
|
scanTTL: 5 * time.Minute,
|
|
}
|
|
}
|
|
|
|
func seedCertWithKey(t *testing.T, db *gorm.DB, encSvc *crypto.EncryptionService, uuid, name, domain string, expiry time.Time) models.SSLCertificate {
|
|
t.Helper()
|
|
certPEM, keyPEM := generateTestCertAndKey(t, domain, expiry)
|
|
|
|
encKey, err := encSvc.Encrypt(keyPEM)
|
|
require.NoError(t, err)
|
|
|
|
cert := models.SSLCertificate{
|
|
UUID: uuid,
|
|
Name: name,
|
|
Provider: "custom",
|
|
Domains: domain,
|
|
CommonName: domain,
|
|
Certificate: string(certPEM),
|
|
PrivateKeyEncrypted: encKey,
|
|
ExpiresAt: &expiry,
|
|
}
|
|
require.NoError(t, db.Create(&cert).Error)
|
|
return cert
|
|
}
|
|
|
|
func TestCertificateService_GetCertificate(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.AutoMigrate(&models.SSLCertificate{}, &models.ProxyHost{}))
|
|
|
|
cs := newTestCertificateService(tmpDir, db)
|
|
|
|
t.Run("not found", func(t *testing.T) {
|
|
_, err := cs.GetCertificate("nonexistent-uuid")
|
|
assert.ErrorIs(t, err, ErrCertNotFound)
|
|
})
|
|
|
|
t.Run("found with no hosts", func(t *testing.T) {
|
|
expiry := time.Now().Add(30 * 24 * time.Hour)
|
|
notBefore := time.Now().Add(-time.Hour)
|
|
cert := models.SSLCertificate{
|
|
UUID: "get-cert-1",
|
|
Name: "Test Cert",
|
|
Provider: "custom",
|
|
Domains: "get.example.com",
|
|
CommonName: "get.example.com",
|
|
ExpiresAt: &expiry,
|
|
NotBefore: ¬Before,
|
|
}
|
|
require.NoError(t, db.Create(&cert).Error)
|
|
|
|
detail, err := cs.GetCertificate("get-cert-1")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "get-cert-1", detail.UUID)
|
|
assert.Equal(t, "Test Cert", detail.Name)
|
|
assert.Equal(t, "get.example.com", detail.CommonName)
|
|
assert.False(t, detail.InUse)
|
|
assert.Empty(t, detail.AssignedHosts)
|
|
})
|
|
|
|
t.Run("found with assigned host", func(t *testing.T) {
|
|
expiry := time.Now().Add(30 * 24 * time.Hour)
|
|
cert := models.SSLCertificate{
|
|
UUID: "get-cert-2",
|
|
Name: "Assigned Cert",
|
|
Provider: "custom",
|
|
Domains: "assigned.example.com",
|
|
CommonName: "assigned.example.com",
|
|
ExpiresAt: &expiry,
|
|
}
|
|
require.NoError(t, db.Create(&cert).Error)
|
|
|
|
ph := models.ProxyHost{
|
|
UUID: "ph-assigned",
|
|
Name: "My Proxy",
|
|
DomainNames: "assigned.example.com",
|
|
ForwardHost: "localhost",
|
|
ForwardPort: 8080,
|
|
CertificateID: &cert.ID,
|
|
}
|
|
require.NoError(t, db.Create(&ph).Error)
|
|
|
|
detail, err := cs.GetCertificate("get-cert-2")
|
|
require.NoError(t, err)
|
|
assert.True(t, detail.InUse)
|
|
require.Len(t, detail.AssignedHosts, 1)
|
|
assert.Equal(t, "My Proxy", detail.AssignedHosts[0].Name)
|
|
})
|
|
|
|
t.Run("nil expiry and not_before", func(t *testing.T) {
|
|
cert := models.SSLCertificate{
|
|
UUID: "get-cert-3",
|
|
Name: "No Dates",
|
|
Provider: "custom",
|
|
Domains: "nodates.example.com",
|
|
}
|
|
require.NoError(t, db.Create(&cert).Error)
|
|
|
|
detail, err := cs.GetCertificate("get-cert-3")
|
|
require.NoError(t, err)
|
|
assert.True(t, detail.ExpiresAt.IsZero())
|
|
assert.True(t, detail.NotBefore.IsZero())
|
|
})
|
|
}
|
|
|
|
func TestCertificateService_ValidateCertificate(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.AutoMigrate(&models.SSLCertificate{}, &models.ProxyHost{}))
|
|
|
|
cs := newTestCertificateService(tmpDir, db)
|
|
|
|
t.Run("valid cert with key", func(t *testing.T) {
|
|
certPEM, keyPEM := generateTestCertAndKey(t, "validate.example.com", time.Now().Add(24*time.Hour))
|
|
result, err := cs.ValidateCertificate(string(certPEM), string(keyPEM), "")
|
|
require.NoError(t, err)
|
|
assert.True(t, result.Valid)
|
|
assert.True(t, result.KeyMatch)
|
|
assert.Empty(t, result.Errors)
|
|
})
|
|
|
|
t.Run("invalid cert data", func(t *testing.T) {
|
|
result, err := cs.ValidateCertificate("not-a-cert", "", "")
|
|
require.NoError(t, err)
|
|
assert.False(t, result.Valid)
|
|
assert.NotEmpty(t, result.Errors)
|
|
})
|
|
|
|
t.Run("valid cert without key", func(t *testing.T) {
|
|
certPEM := generateTestCert(t, "nokey.example.com", time.Now().Add(24*time.Hour))
|
|
result, err := cs.ValidateCertificate(string(certPEM), "", "")
|
|
require.NoError(t, err)
|
|
assert.True(t, result.Valid)
|
|
assert.False(t, result.KeyMatch)
|
|
assert.Empty(t, result.Errors)
|
|
})
|
|
|
|
t.Run("expired cert", func(t *testing.T) {
|
|
certPEM := generateTestCert(t, "expired.example.com", time.Now().Add(-24*time.Hour))
|
|
result, err := cs.ValidateCertificate(string(certPEM), "", "")
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, result.Warnings)
|
|
})
|
|
}
|
|
|
|
func TestCertificateService_UpdateCertificate(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.AutoMigrate(&models.SSLCertificate{}, &models.ProxyHost{}))
|
|
|
|
cs := newTestCertificateService(tmpDir, db)
|
|
|
|
t.Run("not found", func(t *testing.T) {
|
|
_, err := cs.UpdateCertificate("nonexistent-uuid", "New Name")
|
|
assert.ErrorIs(t, err, ErrCertNotFound)
|
|
})
|
|
|
|
t.Run("successful rename", func(t *testing.T) {
|
|
expiry := time.Now().Add(30 * 24 * time.Hour)
|
|
cert := models.SSLCertificate{
|
|
UUID: "update-cert-1",
|
|
Name: "Old Name",
|
|
Provider: "custom",
|
|
Domains: "update.example.com",
|
|
CommonName: "update.example.com",
|
|
ExpiresAt: &expiry,
|
|
}
|
|
require.NoError(t, db.Create(&cert).Error)
|
|
|
|
info, err := cs.UpdateCertificate("update-cert-1", "New Name")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "New Name", info.Name)
|
|
assert.Equal(t, "update-cert-1", info.UUID)
|
|
assert.Equal(t, "custom", info.Provider)
|
|
})
|
|
|
|
t.Run("updates persist", func(t *testing.T) {
|
|
var cert models.SSLCertificate
|
|
require.NoError(t, db.Where("uuid = ?", "update-cert-1").First(&cert).Error)
|
|
assert.Equal(t, "New Name", cert.Name)
|
|
})
|
|
|
|
t.Run("nil expiry and not_before", func(t *testing.T) {
|
|
cert := models.SSLCertificate{
|
|
UUID: "update-cert-2",
|
|
Name: "No Dates Cert",
|
|
Provider: "custom",
|
|
Domains: "nodates-update.example.com",
|
|
}
|
|
require.NoError(t, db.Create(&cert).Error)
|
|
|
|
info, err := cs.UpdateCertificate("update-cert-2", "Renamed No Dates")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Renamed No Dates", info.Name)
|
|
assert.True(t, info.ExpiresAt.IsZero())
|
|
})
|
|
}
|
|
|
|
func TestCertificateService_IsCertificateInUseByUUID(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.AutoMigrate(&models.SSLCertificate{}, &models.ProxyHost{}))
|
|
|
|
cs := newTestCertificateService(tmpDir, db)
|
|
|
|
t.Run("not found", func(t *testing.T) {
|
|
_, err := cs.IsCertificateInUseByUUID("nonexistent-uuid")
|
|
assert.ErrorIs(t, err, ErrCertNotFound)
|
|
})
|
|
|
|
t.Run("not in use", func(t *testing.T) {
|
|
cert := models.SSLCertificate{UUID: "inuse-1", Name: "Free Cert", Provider: "custom", Domains: "free.example.com"}
|
|
require.NoError(t, db.Create(&cert).Error)
|
|
|
|
inUse, err := cs.IsCertificateInUseByUUID("inuse-1")
|
|
require.NoError(t, err)
|
|
assert.False(t, inUse)
|
|
})
|
|
|
|
t.Run("in use", func(t *testing.T) {
|
|
cert := models.SSLCertificate{UUID: "inuse-2", Name: "Used Cert", Provider: "custom", Domains: "used.example.com"}
|
|
require.NoError(t, db.Create(&cert).Error)
|
|
|
|
ph := models.ProxyHost{UUID: "ph-inuse", Name: "Using Proxy", DomainNames: "used.example.com", ForwardHost: "localhost", ForwardPort: 8080, CertificateID: &cert.ID}
|
|
require.NoError(t, db.Create(&ph).Error)
|
|
|
|
inUse, err := cs.IsCertificateInUseByUUID("inuse-2")
|
|
require.NoError(t, err)
|
|
assert.True(t, inUse)
|
|
})
|
|
}
|
|
|
|
func TestCertificateService_DeleteCertificateByID(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.AutoMigrate(&models.SSLCertificate{}, &models.ProxyHost{}))
|
|
|
|
cs := newTestCertificateService(tmpDir, db)
|
|
|
|
cert := models.SSLCertificate{UUID: "del-by-id-1", Name: "Delete By ID", Provider: "custom", Domains: "delbyid.example.com"}
|
|
require.NoError(t, db.Create(&cert).Error)
|
|
|
|
err = cs.DeleteCertificateByID(cert.ID)
|
|
require.NoError(t, err)
|
|
|
|
var found models.SSLCertificate
|
|
err = db.Where("uuid = ?", "del-by-id-1").First(&found).Error
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestCertificateService_ExportCertificate(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.AutoMigrate(&models.SSLCertificate{}, &models.ProxyHost{}))
|
|
|
|
encSvc := newTestEncryptionService(t)
|
|
cs := newTestCertServiceWithEnc(t, tmpDir, db)
|
|
|
|
domain := "export.example.com"
|
|
expiry := time.Now().Add(30 * 24 * time.Hour)
|
|
cert := seedCertWithKey(t, db, encSvc, "export-cert-1", "Export Cert", domain, expiry)
|
|
|
|
t.Run("not found", func(t *testing.T) {
|
|
_, _, err := cs.ExportCertificate("nonexistent", "pem", false)
|
|
assert.ErrorIs(t, err, ErrCertNotFound)
|
|
})
|
|
|
|
t.Run("pem without key", func(t *testing.T) {
|
|
data, filename, err := cs.ExportCertificate(cert.UUID, "pem", false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Export Cert.pem", filename)
|
|
assert.Contains(t, string(data), "BEGIN CERTIFICATE")
|
|
})
|
|
|
|
t.Run("pem with key", func(t *testing.T) {
|
|
data, filename, err := cs.ExportCertificate(cert.UUID, "pem", true)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Export Cert.pem", filename)
|
|
assert.Contains(t, string(data), "BEGIN CERTIFICATE")
|
|
assert.Contains(t, string(data), "PRIVATE KEY")
|
|
})
|
|
|
|
t.Run("der format", func(t *testing.T) {
|
|
data, filename, err := cs.ExportCertificate(cert.UUID, "der", false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Export Cert.der", filename)
|
|
assert.NotEmpty(t, data)
|
|
})
|
|
|
|
t.Run("pfx format", func(t *testing.T) {
|
|
data, filename, err := cs.ExportCertificate(cert.UUID, "pfx", false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Export Cert.pfx", filename)
|
|
assert.NotEmpty(t, data)
|
|
})
|
|
|
|
t.Run("unsupported format", func(t *testing.T) {
|
|
_, _, err := cs.ExportCertificate(cert.UUID, "jks", false)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "unsupported export format")
|
|
})
|
|
|
|
t.Run("empty name uses fallback", func(t *testing.T) {
|
|
noNameCert := seedCertWithKey(t, db, encSvc, "export-noname", "", domain, expiry)
|
|
_, filename, err := cs.ExportCertificate(noNameCert.UUID, "pem", false)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "certificate.pem", filename)
|
|
})
|
|
}
|
|
|
|
func TestCertificateService_GetDecryptedPrivateKey(t *testing.T) {
|
|
encSvc := newTestEncryptionService(t)
|
|
|
|
t.Run("no encrypted key", func(t *testing.T) {
|
|
cs := &CertificateService{encSvc: encSvc}
|
|
cert := &models.SSLCertificate{PrivateKeyEncrypted: ""}
|
|
_, err := cs.GetDecryptedPrivateKey(cert)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "no encrypted private key")
|
|
})
|
|
|
|
t.Run("no encryption service", func(t *testing.T) {
|
|
cs := &CertificateService{encSvc: nil}
|
|
cert := &models.SSLCertificate{PrivateKeyEncrypted: "some-data"}
|
|
_, err := cs.GetDecryptedPrivateKey(cert)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "encryption service not configured")
|
|
})
|
|
|
|
t.Run("successful decryption", func(t *testing.T) {
|
|
cs := &CertificateService{encSvc: encSvc}
|
|
plaintext := "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----" //nolint:gosec // test data, not real credentials
|
|
encrypted, err := encSvc.Encrypt([]byte(plaintext))
|
|
require.NoError(t, err)
|
|
|
|
cert := &models.SSLCertificate{PrivateKeyEncrypted: encrypted}
|
|
result, err := cs.GetDecryptedPrivateKey(cert)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, plaintext, result)
|
|
})
|
|
}
|
|
|
|
func TestCertificateService_CheckExpiringCertificates(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.AutoMigrate(&models.SSLCertificate{}, &models.ProxyHost{}))
|
|
|
|
cs := newTestCertificateService(tmpDir, db)
|
|
|
|
// Create certs with different expiry states
|
|
expiringSoon := time.Now().Add(5 * 24 * time.Hour)
|
|
expired := time.Now().Add(-24 * time.Hour)
|
|
farFuture := time.Now().Add(365 * 24 * time.Hour)
|
|
|
|
db.Create(&models.SSLCertificate{UUID: "exp-soon", Name: "Expiring Soon", Provider: "custom", Domains: "soon.example.com", ExpiresAt: &expiringSoon})
|
|
db.Create(&models.SSLCertificate{UUID: "exp-past", Name: "Already Expired", Provider: "custom", Domains: "expired.example.com", ExpiresAt: &expired})
|
|
db.Create(&models.SSLCertificate{UUID: "exp-far", Name: "Far Future", Provider: "custom", Domains: "far.example.com", ExpiresAt: &farFuture})
|
|
// ACME certs should not be included (only custom)
|
|
db.Create(&models.SSLCertificate{UUID: "exp-le", Name: "LE Cert", Provider: "letsencrypt", Domains: "le.example.com", ExpiresAt: &expiringSoon})
|
|
|
|
t.Run("30 day window", func(t *testing.T) {
|
|
certs, err := cs.CheckExpiringCertificates(30)
|
|
require.NoError(t, err)
|
|
assert.Len(t, certs, 2) // expiringSoon and expired
|
|
|
|
foundSoon := false
|
|
foundExpired := false
|
|
for _, c := range certs {
|
|
if c.UUID == "exp-soon" {
|
|
foundSoon = true
|
|
}
|
|
if c.UUID == "exp-past" {
|
|
foundExpired = true
|
|
}
|
|
}
|
|
assert.True(t, foundSoon)
|
|
assert.True(t, foundExpired)
|
|
})
|
|
|
|
t.Run("1 day window", func(t *testing.T) {
|
|
certs, err := cs.CheckExpiringCertificates(1)
|
|
require.NoError(t, err)
|
|
assert.Len(t, certs, 1) // only the expired one
|
|
assert.Equal(t, "exp-past", certs[0].UUID)
|
|
})
|
|
}
|
|
|
|
func TestCertificateService_CheckExpiry(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.AutoMigrate(&models.SSLCertificate{}, &models.ProxyHost{}, &models.Setting{}, &models.NotificationProvider{}, &models.Notification{}))
|
|
|
|
cs := newTestCertificateService(tmpDir, db)
|
|
ns := NewNotificationService(db, nil)
|
|
|
|
expiringSoon := time.Now().Add(5 * 24 * time.Hour)
|
|
expired := time.Now().Add(-24 * time.Hour)
|
|
db.Create(&models.SSLCertificate{UUID: "chk-soon", Name: "Expiring", Provider: "custom", Domains: "chksoon.example.com", ExpiresAt: &expiringSoon})
|
|
db.Create(&models.SSLCertificate{UUID: "chk-past", Name: "Expired", Provider: "custom", Domains: "chkpast.example.com", ExpiresAt: &expired})
|
|
|
|
t.Run("nil notification service", func(t *testing.T) {
|
|
cs.checkExpiry(context.Background(), nil, 30)
|
|
})
|
|
|
|
t.Run("creates notifications for expiring certs", func(t *testing.T) {
|
|
cs.checkExpiry(context.Background(), ns, 30)
|
|
|
|
var notifications []models.Notification
|
|
db.Find(¬ifications)
|
|
assert.GreaterOrEqual(t, len(notifications), 2)
|
|
})
|
|
}
|
|
|
|
func TestCertificateService_MigratePrivateKeys(t *testing.T) {
|
|
t.Run("no encryption service", func(t *testing.T) {
|
|
cs := &CertificateService{encSvc: nil}
|
|
err := cs.MigratePrivateKeys()
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("no keys to migrate", func(t *testing.T) {
|
|
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.AutoMigrate(&models.SSLCertificate{}, &models.ProxyHost{}))
|
|
// MigratePrivateKeys uses raw SQL referencing private_key column (gorm:"-" tag)
|
|
require.NoError(t, db.Exec("ALTER TABLE ssl_certificates ADD COLUMN private_key TEXT DEFAULT ''").Error)
|
|
|
|
encSvc := newTestEncryptionService(t)
|
|
cs := &CertificateService{db: db, encSvc: encSvc}
|
|
|
|
err = cs.MigratePrivateKeys()
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("migrates plaintext key", func(t *testing.T) {
|
|
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
require.NoError(t, db.AutoMigrate(&models.SSLCertificate{}, &models.ProxyHost{}))
|
|
// MigratePrivateKeys uses raw SQL referencing private_key column (gorm:"-" tag)
|
|
require.NoError(t, db.Exec("ALTER TABLE ssl_certificates ADD COLUMN private_key TEXT DEFAULT ''").Error)
|
|
|
|
// Insert cert with plaintext key using raw SQL{}))
|
|
// MigratePrivateKeys uses raw SQL referencing private_key column (gorm:"-" tag)
|
|
require.NoError(t, db.Exec("ALTER TABLE ssl_certificates ADD COLUMN private_key TEXT DEFAULT ''").Error)
|
|
|
|
// Insert cert with plaintext key using raw SQL
|
|
require.NoError(t, db.Exec(
|
|
"INSERT INTO ssl_certificates (uuid, name, provider, domains, private_key) VALUES (?, ?, ?, ?, ?)",
|
|
"migrate-1", "Migrate Test", "custom", "migrate.example.com", "plaintext-key-data",
|
|
).Error)
|
|
|
|
encSvc := newTestEncryptionService(t)
|
|
cs := &CertificateService{db: db, encSvc: encSvc}
|
|
|
|
err = cs.MigratePrivateKeys()
|
|
require.NoError(t, err)
|
|
|
|
// Verify the key was encrypted and plaintext cleared
|
|
type rawRow struct {
|
|
PrivateKey string `gorm:"column:private_key"`
|
|
PrivateKeyEnc string `gorm:"column:private_key_enc"`
|
|
}
|
|
var row rawRow
|
|
require.NoError(t, db.Raw("SELECT private_key, private_key_enc FROM ssl_certificates WHERE uuid = ?", "migrate-1").Scan(&row).Error)
|
|
assert.Empty(t, row.PrivateKey)
|
|
assert.NotEmpty(t, row.PrivateKeyEnc)
|
|
})
|
|
}
|