mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 12:41:30 +00:00
995b72df05
Replace static env-var-based issuer wiring with GUI-driven dynamic configuration stored encrypted in PostgreSQL. Operators can now configure, test, enable/disable, and manage issuers from the dashboard without restarting the server. Key changes: - AES-256-GCM encryption for sensitive issuer config at rest (PBKDF2 key derivation with 100k iterations) - Dynamic IssuerRegistry with sync.RWMutex replacing static map - Connector factory pattern (issuerfactory.NewFromConfig) replacing 140 lines of static wiring in main.go - Migration 000009: encrypted_config, last_tested_at, test_status, source columns on issuers table - Env var seeding on first boot with ON CONFLICT DO NOTHING - Registry Rebuild() for atomic map swap after CRUD operations - Issuer type validation against domain constants on Create - Audit trail for test connection results - Conditional seeding for step-ca/OpenSSL (only when env vars set) - GUI: source badge, connection test status on issuer detail page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
139 lines
3.9 KiB
Go
139 lines
3.9 KiB
Go
package issuerfactory
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log/slog"
|
|
"os"
|
|
"testing"
|
|
)
|
|
|
|
func testLogger() *slog.Logger {
|
|
return slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
|
|
}
|
|
|
|
func TestNewFromConfig_LocalCA(t *testing.T) {
|
|
cfg := json.RawMessage(`{"ca_common_name":"Test CA"}`)
|
|
conn, err := NewFromConfig("local", cfg, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("NewFromConfig(local) failed: %v", err)
|
|
}
|
|
if conn == nil {
|
|
t.Fatal("expected non-nil connector")
|
|
}
|
|
}
|
|
|
|
func TestNewFromConfig_GenericCA_Alias(t *testing.T) {
|
|
cfg := json.RawMessage(`{}`)
|
|
conn, err := NewFromConfig("GenericCA", cfg, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("NewFromConfig(GenericCA) failed: %v", err)
|
|
}
|
|
if conn == nil {
|
|
t.Fatal("expected non-nil connector")
|
|
}
|
|
}
|
|
|
|
func TestNewFromConfig_ACME(t *testing.T) {
|
|
cfg := json.RawMessage(`{"directory_url":"https://acme-staging-v02.api.letsencrypt.org/directory","email":"test@example.com"}`)
|
|
conn, err := NewFromConfig("ACME", cfg, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("NewFromConfig(ACME) failed: %v", err)
|
|
}
|
|
if conn == nil {
|
|
t.Fatal("expected non-nil connector")
|
|
}
|
|
}
|
|
|
|
func TestNewFromConfig_StepCA(t *testing.T) {
|
|
cfg := json.RawMessage(`{"ca_url":"https://ca.internal:9000","provisioner_name":"test"}`)
|
|
conn, err := NewFromConfig("StepCA", cfg, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("NewFromConfig(StepCA) failed: %v", err)
|
|
}
|
|
if conn == nil {
|
|
t.Fatal("expected non-nil connector")
|
|
}
|
|
}
|
|
|
|
func TestNewFromConfig_OpenSSL(t *testing.T) {
|
|
cfg := json.RawMessage(`{"sign_script":"/path/to/sign.sh"}`)
|
|
conn, err := NewFromConfig("OpenSSL", cfg, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("NewFromConfig(OpenSSL) failed: %v", err)
|
|
}
|
|
if conn == nil {
|
|
t.Fatal("expected non-nil connector")
|
|
}
|
|
}
|
|
|
|
func TestNewFromConfig_VaultPKI(t *testing.T) {
|
|
cfg := json.RawMessage(`{"addr":"https://vault:8200","token":"hvs.test","mount":"pki","role":"web","ttl":"8760h"}`)
|
|
conn, err := NewFromConfig("VaultPKI", cfg, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("NewFromConfig(VaultPKI) failed: %v", err)
|
|
}
|
|
if conn == nil {
|
|
t.Fatal("expected non-nil connector")
|
|
}
|
|
}
|
|
|
|
func TestNewFromConfig_DigiCert(t *testing.T) {
|
|
cfg := json.RawMessage(`{"api_key":"test-key","org_id":"123","product_type":"ssl_basic"}`)
|
|
conn, err := NewFromConfig("DigiCert", cfg, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("NewFromConfig(DigiCert) failed: %v", err)
|
|
}
|
|
if conn == nil {
|
|
t.Fatal("expected non-nil connector")
|
|
}
|
|
}
|
|
|
|
func TestNewFromConfig_Sectigo(t *testing.T) {
|
|
cfg := json.RawMessage(`{"customer_uri":"test-org","login":"api-user","password":"secret","org_id":1}`)
|
|
conn, err := NewFromConfig("Sectigo", cfg, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("NewFromConfig(Sectigo) failed: %v", err)
|
|
}
|
|
if conn == nil {
|
|
t.Fatal("expected non-nil connector")
|
|
}
|
|
}
|
|
|
|
func TestNewFromConfig_GoogleCAS(t *testing.T) {
|
|
cfg := json.RawMessage(`{"project":"my-project","location":"us-central1","ca_pool":"my-pool","credentials":"/path/to/creds.json"}`)
|
|
conn, err := NewFromConfig("GoogleCAS", cfg, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("NewFromConfig(GoogleCAS) failed: %v", err)
|
|
}
|
|
if conn == nil {
|
|
t.Fatal("expected non-nil connector")
|
|
}
|
|
}
|
|
|
|
func TestNewFromConfig_UnknownType(t *testing.T) {
|
|
cfg := json.RawMessage(`{}`)
|
|
_, err := NewFromConfig("UnknownCA", cfg, testLogger())
|
|
if err == nil {
|
|
t.Fatal("expected error for unknown type")
|
|
}
|
|
}
|
|
|
|
func TestNewFromConfig_MalformedJSON(t *testing.T) {
|
|
cfg := json.RawMessage(`{invalid json}`)
|
|
_, err := NewFromConfig("ACME", cfg, testLogger())
|
|
if err == nil {
|
|
t.Fatal("expected error for malformed JSON")
|
|
}
|
|
}
|
|
|
|
func TestNewFromConfig_EmptyConfig(t *testing.T) {
|
|
// Empty config should work — connectors have defaults
|
|
conn, err := NewFromConfig("local", nil, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("NewFromConfig with nil config failed: %v", err)
|
|
}
|
|
if conn == nil {
|
|
t.Fatal("expected non-nil connector")
|
|
}
|
|
}
|