597 lines
21 KiB
Go
597 lines
21 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"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/models"
|
|
)
|
|
|
|
// --- ExportCertificate DER format ---
|
|
|
|
func TestExportCertificate_DER(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)
|
|
certPEM, keyPEM := generateTestCertAndKey(t, "der-export.example.com", time.Now().Add(24*time.Hour))
|
|
info, err := cs.UploadCertificate("der-export", string(certPEM), string(keyPEM), "")
|
|
require.NoError(t, err)
|
|
|
|
data, filename, err := cs.ExportCertificate(info.UUID, "der", false, "")
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, data)
|
|
assert.Contains(t, filename, ".der")
|
|
}
|
|
|
|
// --- ExportCertificate PFX format ---
|
|
|
|
func TestExportCertificate_PFX(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 := newTestCertServiceWithEnc(t, tmpDir, db)
|
|
certPEM, keyPEM := generateTestCertAndKey(t, "pfx-export.example.com", time.Now().Add(24*time.Hour))
|
|
info, err := cs.UploadCertificate("pfx-export", string(certPEM), string(keyPEM), "")
|
|
require.NoError(t, err)
|
|
|
|
data, filename, err := cs.ExportCertificate(info.UUID, "pfx", true, "test-password")
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, data)
|
|
assert.Contains(t, filename, ".pfx")
|
|
}
|
|
|
|
func TestExportCertificate_P12(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 := newTestCertServiceWithEnc(t, tmpDir, db)
|
|
certPEM, keyPEM := generateTestCertAndKey(t, "p12-export.example.com", time.Now().Add(24*time.Hour))
|
|
info, err := cs.UploadCertificate("p12-export", string(certPEM), string(keyPEM), "")
|
|
require.NoError(t, err)
|
|
|
|
data, filename, err := cs.ExportCertificate(info.UUID, "p12", true, "password")
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, data)
|
|
assert.Contains(t, filename, ".pfx")
|
|
}
|
|
|
|
func TestExportCertificate_UnsupportedFormat(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)
|
|
certPEM, keyPEM := generateTestCertAndKey(t, "unsupported.example.com", time.Now().Add(24*time.Hour))
|
|
info, err := cs.UploadCertificate("unsupported-fmt", string(certPEM), string(keyPEM), "")
|
|
require.NoError(t, err)
|
|
|
|
_, _, err = cs.ExportCertificate(info.UUID, "xml", false, "")
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "unsupported export format")
|
|
}
|
|
|
|
func TestExportCertificate_PEMWithKey(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 := newTestCertServiceWithEnc(t, tmpDir, db)
|
|
certPEM, keyPEM := generateTestCertAndKey(t, "pem-key.example.com", time.Now().Add(24*time.Hour))
|
|
info, err := cs.UploadCertificate("pem-key-export", string(certPEM), string(keyPEM), "")
|
|
require.NoError(t, err)
|
|
|
|
data, filename, err := cs.ExportCertificate(info.UUID, "pem", true, "")
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(data), "PRIVATE KEY")
|
|
assert.Contains(t, filename, ".pem")
|
|
}
|
|
|
|
func TestExportCertificate_NotFound(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)
|
|
_, _, err = cs.ExportCertificate("nonexistent-uuid", "pem", false, "")
|
|
assert.ErrorIs(t, err, ErrCertNotFound)
|
|
}
|
|
|
|
// --- GetDecryptedPrivateKey ---
|
|
|
|
func TestGetDecryptedPrivateKey_NoEncryptedKey(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{PrivateKeyEncrypted: ""}
|
|
_, err = cs.GetDecryptedPrivateKey(cert)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "no encrypted private key")
|
|
}
|
|
|
|
func TestGetDecryptedPrivateKey_NoEncryptionService(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) // no encSvc
|
|
cert := &models.SSLCertificate{PrivateKeyEncrypted: "some-encrypted-data"}
|
|
_, err = cs.GetDecryptedPrivateKey(cert)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "encryption service not configured")
|
|
}
|
|
|
|
// --- MigratePrivateKeys ---
|
|
|
|
func TestMigratePrivateKeys_NoEncryptionService(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)
|
|
err = cs.MigratePrivateKeys()
|
|
assert.NoError(t, err) // should return nil without error
|
|
}
|
|
|
|
func TestMigratePrivateKeys_NoCertsToMigrate(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{}))
|
|
// MigratePrivateKeys uses raw SQL against private_key column (gorm:"-"), so add it manually
|
|
db.Exec("ALTER TABLE ssl_certificates ADD COLUMN private_key TEXT DEFAULT ''")
|
|
|
|
cs := newTestCertServiceWithEnc(t, tmpDir, db)
|
|
err = cs.MigratePrivateKeys()
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestMigratePrivateKeys_WithPlaintextKey(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{}))
|
|
// MigratePrivateKeys uses raw SQL against private_key column (gorm:"-"), so add it manually
|
|
db.Exec("ALTER TABLE ssl_certificates ADD COLUMN private_key TEXT DEFAULT ''")
|
|
cs := newTestCertServiceWithEnc(t, tmpDir, db)
|
|
_, keyPEM := generateTestCertAndKey(t, "migrate.example.com", time.Now().Add(24*time.Hour))
|
|
|
|
// Insert a cert with plaintext private_key via raw SQL
|
|
db.Exec("INSERT INTO ssl_certificates (uuid, name, provider, domains, common_name, private_key) VALUES (?, ?, ?, ?, ?, ?)",
|
|
"migrate-uuid", "Migrate Test", "custom", "migrate.example.com", "migrate.example.com", string(keyPEM))
|
|
|
|
err = cs.MigratePrivateKeys()
|
|
assert.NoError(t, err)
|
|
|
|
// Verify the key was encrypted
|
|
var encKey string
|
|
db.Raw("SELECT private_key_enc FROM ssl_certificates WHERE uuid = ?", "migrate-uuid").Scan(&encKey)
|
|
assert.NotEmpty(t, encKey)
|
|
|
|
// Verify plaintext key was cleared
|
|
var plainKey string
|
|
db.Raw("SELECT private_key FROM ssl_certificates WHERE uuid = ?", "migrate-uuid").Scan(&plainKey)
|
|
assert.Empty(t, plainKey)
|
|
}
|
|
|
|
// --- DeleteCertificateByID ---
|
|
|
|
func TestDeleteCertificateByID_Success(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)
|
|
certPEM, keyPEM := generateTestCertAndKey(t, "byid.example.com", time.Now().Add(24*time.Hour))
|
|
info, err := cs.UploadCertificate("by-id-delete", string(certPEM), string(keyPEM), "")
|
|
require.NoError(t, err)
|
|
|
|
var stored models.SSLCertificate
|
|
require.NoError(t, db.Where("uuid = ?", info.UUID).First(&stored).Error)
|
|
|
|
err = cs.DeleteCertificateByID(stored.ID)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestDeleteCertificateByID_NotFound(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)
|
|
err = cs.DeleteCertificateByID(99999)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
// --- UpdateCertificate ---
|
|
|
|
func TestUpdateCertificate_Success(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)
|
|
certPEM, keyPEM := generateTestCertAndKey(t, "update.example.com", time.Now().Add(24*time.Hour))
|
|
info, err := cs.UploadCertificate("old-name", string(certPEM), string(keyPEM), "")
|
|
require.NoError(t, err)
|
|
|
|
updated, err := cs.UpdateCertificate(info.UUID, "new-name")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "new-name", updated.Name)
|
|
}
|
|
|
|
func TestUpdateCertificate_NotFound(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)
|
|
_, err = cs.UpdateCertificate("nonexistent", "name")
|
|
assert.ErrorIs(t, err, ErrCertNotFound)
|
|
}
|
|
|
|
// --- IsCertificateInUseByUUID ---
|
|
|
|
func TestIsCertificateInUseByUUID_NotFound(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)
|
|
_, err = cs.IsCertificateInUseByUUID("nonexistent-uuid")
|
|
assert.ErrorIs(t, err, ErrCertNotFound)
|
|
}
|
|
|
|
func TestIsCertificateInUseByUUID_NotInUse(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)
|
|
certPEM, keyPEM := generateTestCertAndKey(t, "inuse-uuid.example.com", time.Now().Add(24*time.Hour))
|
|
info, err := cs.UploadCertificate("uuid-inuse-test", string(certPEM), string(keyPEM), "")
|
|
require.NoError(t, err)
|
|
|
|
inUse, err := cs.IsCertificateInUseByUUID(info.UUID)
|
|
require.NoError(t, err)
|
|
assert.False(t, inUse)
|
|
}
|
|
|
|
// --- CheckExpiringCertificates ---
|
|
|
|
func TestCheckExpiringCertificates_WithExpiring(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 a cert expiring in 10 days
|
|
expiry := time.Now().Add(10 * 24 * time.Hour)
|
|
notBefore := time.Now().Add(-24 * time.Hour)
|
|
db.Create(&models.SSLCertificate{
|
|
UUID: "expiring-uuid", Name: "Expiring Cert", Provider: "custom",
|
|
Domains: "expiring.example.com", CommonName: "expiring.example.com",
|
|
ExpiresAt: &expiry, NotBefore: ¬Before,
|
|
})
|
|
|
|
certs, err := cs.CheckExpiringCertificates(30)
|
|
require.NoError(t, err)
|
|
assert.Len(t, certs, 1)
|
|
assert.Equal(t, "Expiring Cert", certs[0].Name)
|
|
assert.Equal(t, "expiring", certs[0].Status)
|
|
}
|
|
|
|
func TestCheckExpiringCertificates_WithExpired(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)
|
|
|
|
expiry := time.Now().Add(-24 * time.Hour)
|
|
db.Create(&models.SSLCertificate{
|
|
UUID: "expired-uuid", Name: "Expired Cert", Provider: "custom",
|
|
Domains: "expired.example.com", CommonName: "expired.example.com",
|
|
ExpiresAt: &expiry,
|
|
})
|
|
|
|
certs, err := cs.CheckExpiringCertificates(30)
|
|
require.NoError(t, err)
|
|
assert.Len(t, certs, 1)
|
|
assert.Equal(t, "expired", certs[0].Status)
|
|
}
|
|
|
|
func TestCheckExpiringCertificates_NoneExpiring(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 expiring in 90 days - outside 30 day window
|
|
expiry := time.Now().Add(90 * 24 * time.Hour)
|
|
db.Create(&models.SSLCertificate{
|
|
UUID: "valid-uuid", Name: "Valid Cert", Provider: "custom",
|
|
Domains: "valid.example.com", CommonName: "valid.example.com",
|
|
ExpiresAt: &expiry,
|
|
})
|
|
|
|
certs, err := cs.CheckExpiringCertificates(30)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, certs)
|
|
}
|
|
|
|
// --- checkExpiry with notification service ---
|
|
|
|
func TestCheckExpiry_WithExpiringCerts(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)
|
|
|
|
// Create expiring cert
|
|
expiry := time.Now().Add(10 * 24 * time.Hour)
|
|
db.Create(&models.SSLCertificate{
|
|
UUID: "notify-expiring", Name: "Notify Cert", Provider: "custom",
|
|
Domains: "notify.example.com", CommonName: "notify.example.com",
|
|
ExpiresAt: &expiry,
|
|
})
|
|
|
|
notifSvc := NewNotificationService(db, nil)
|
|
cs.checkExpiry(context.Background(), notifSvc, 30)
|
|
|
|
// Verify a notification was created
|
|
var count int64
|
|
db.Model(&models.Notification{}).Count(&count)
|
|
assert.Greater(t, count, int64(0))
|
|
}
|
|
|
|
func TestCheckExpiry_WithExpiredCerts(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)
|
|
|
|
expiry := time.Now().Add(-24 * time.Hour)
|
|
db.Create(&models.SSLCertificate{
|
|
UUID: "notify-expired", Name: "Expired Notify", Provider: "custom",
|
|
Domains: "expired-notify.example.com", CommonName: "expired-notify.example.com",
|
|
ExpiresAt: &expiry,
|
|
})
|
|
|
|
notifSvc := NewNotificationService(db, nil)
|
|
cs.checkExpiry(context.Background(), notifSvc, 30)
|
|
|
|
var count int64
|
|
db.Model(&models.Notification{}).Count(&count)
|
|
assert.Greater(t, count, int64(0))
|
|
}
|
|
|
|
// --- ListCertificates with chain and proxy host ---
|
|
|
|
func TestListCertificates_WithChainAndProxyHost(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)
|
|
|
|
certPEM, _, err := generateSelfSignedCertPEM()
|
|
require.NoError(t, err)
|
|
|
|
chainPEM := certPEM + "\n" + certPEM
|
|
|
|
expiry := time.Now().Add(90 * 24 * time.Hour)
|
|
notBefore := time.Now().Add(-1 * time.Hour)
|
|
certID := uint(99)
|
|
db.Create(&models.SSLCertificate{
|
|
ID: certID,
|
|
UUID: "chain-test-uuid",
|
|
Name: "Chain Test",
|
|
Provider: "custom",
|
|
Domains: "chain.example.com",
|
|
CommonName: "chain.example.com",
|
|
Certificate: certPEM,
|
|
CertificateChain: chainPEM,
|
|
ExpiresAt: &expiry,
|
|
NotBefore: ¬Before,
|
|
})
|
|
|
|
db.Create(&models.ProxyHost{
|
|
Name: "My Proxy",
|
|
DomainNames: "chain.example.com",
|
|
CertificateID: &certID,
|
|
})
|
|
|
|
certs, err := cs.ListCertificates()
|
|
require.NoError(t, err)
|
|
require.Len(t, certs, 1)
|
|
assert.Equal(t, 2, certs[0].ChainDepth)
|
|
assert.True(t, certs[0].InUse)
|
|
assert.Equal(t, "chain-test-uuid", certs[0].UUID)
|
|
}
|
|
|
|
// --- UploadCertificate with key ---
|
|
|
|
func TestUploadCertificate_WithKey(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 := newTestCertServiceWithEnc(t, tmpDir, db)
|
|
|
|
certPEM, keyPEM, err := generateSelfSignedCertPEM()
|
|
require.NoError(t, err)
|
|
|
|
info, err := cs.UploadCertificate("My Upload", certPEM, keyPEM, "")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, info)
|
|
assert.Equal(t, "My Upload", info.Name)
|
|
assert.True(t, info.HasKey)
|
|
assert.NotEmpty(t, info.UUID)
|
|
assert.Equal(t, "custom", info.Provider)
|
|
}
|
|
|
|
// --- ValidateCertificate with key match ---
|
|
|
|
func TestValidateCertificate_WithKeyMatch(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)
|
|
|
|
certPEM, keyPEM, err := generateSelfSignedCertPEM()
|
|
require.NoError(t, err)
|
|
|
|
result, err := cs.ValidateCertificate(certPEM, keyPEM, "")
|
|
require.NoError(t, err)
|
|
assert.True(t, result.Valid)
|
|
assert.True(t, result.KeyMatch)
|
|
assert.Empty(t, result.Errors)
|
|
assert.Contains(t, result.Warnings, "certificate could not be verified against system roots")
|
|
}
|
|
|
|
// --- UpdateCertificate with chain depth ---
|
|
|
|
func TestUpdateCertificate_WithChainDepth(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)
|
|
|
|
certPEM, _, err := generateSelfSignedCertPEM()
|
|
require.NoError(t, err)
|
|
|
|
chainPEM := certPEM + "\n" + certPEM + "\n" + certPEM
|
|
|
|
expiry := time.Now().Add(90 * 24 * time.Hour)
|
|
db.Create(&models.SSLCertificate{
|
|
UUID: "update-chain-uuid",
|
|
Name: "Chain Update",
|
|
Provider: "custom",
|
|
Domains: "update-chain.example.com",
|
|
CommonName: "update-chain.example.com",
|
|
Certificate: certPEM,
|
|
CertificateChain: chainPEM,
|
|
ExpiresAt: &expiry,
|
|
})
|
|
|
|
info, err := cs.UpdateCertificate("update-chain-uuid", "Renamed Chain")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Renamed Chain", info.Name)
|
|
assert.Equal(t, 3, info.ChainDepth)
|
|
}
|
|
|
|
// --- ExportCertificate PEM with chain ---
|
|
|
|
func TestExportCertificate_PEMWithChain(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 := newTestCertServiceWithEnc(t, tmpDir, db)
|
|
|
|
certPEM, keyPEM, err := generateSelfSignedCertPEM()
|
|
require.NoError(t, err)
|
|
|
|
encSvc := newTestEncryptionService(t)
|
|
encKey, err := encSvc.Encrypt([]byte(keyPEM))
|
|
require.NoError(t, err)
|
|
|
|
chainPEM := certPEM
|
|
|
|
db.Create(&models.SSLCertificate{
|
|
UUID: "export-chain-uuid",
|
|
Name: "Export Chain",
|
|
Provider: "custom",
|
|
Domains: "export-chain.example.com",
|
|
CommonName: "export-chain.example.com",
|
|
Certificate: certPEM,
|
|
CertificateChain: chainPEM,
|
|
PrivateKeyEncrypted: encKey,
|
|
})
|
|
|
|
data, filename, err := cs.ExportCertificate("export-chain-uuid", "pem", true, "")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Export Chain.pem", filename)
|
|
assert.Contains(t, string(data), "BEGIN CERTIFICATE")
|
|
assert.Contains(t, string(data), "BEGIN")
|
|
}
|