mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 16:21:30 +00:00
feat(M34): dynamic issuer configuration with encrypted config storage
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>
This commit is contained in:
@@ -43,9 +43,8 @@ func TestCertificateLifecycle(t *testing.T) {
|
||||
localCA := local.New(nil, logger)
|
||||
|
||||
// Build issuer registry with adapter
|
||||
issuerRegistry := map[string]service.IssuerConnector{
|
||||
"iss-local": service.NewIssuerConnectorAdapter(localCA),
|
||||
}
|
||||
issuerRegistry := service.NewIssuerRegistry(logger)
|
||||
issuerRegistry.Set("iss-local", service.NewIssuerConnectorAdapter(localCA))
|
||||
|
||||
// Initialize services (following dependency graph)
|
||||
auditService := service.NewAuditService(auditRepo)
|
||||
@@ -67,7 +66,7 @@ func TestCertificateLifecycle(t *testing.T) {
|
||||
deploymentService := service.NewDeploymentService(jobRepo, targetRepo, agentRepo, certRepo, auditService, notificationService)
|
||||
jobService := service.NewJobService(jobRepo, renewalService, deploymentService, logger)
|
||||
agentService := service.NewAgentService(agentRepo, certRepo, jobRepo, targetRepo, auditService, issuerRegistry, renewalService)
|
||||
issuerService := service.NewIssuerService(issuerRepo, auditService)
|
||||
issuerService := service.NewIssuerService(issuerRepo, auditService, issuerRegistry, nil, slog.Default())
|
||||
|
||||
// Initialize handlers
|
||||
certificateHandler := handler.NewCertificateHandler(certificateService)
|
||||
@@ -90,7 +89,8 @@ func TestCertificateLifecycle(t *testing.T) {
|
||||
verificationHandler := handler.NewVerificationHandler(&mockVerificationService{})
|
||||
|
||||
// EST handler — uses real Local CA issuer via ESTService
|
||||
estService := service.NewESTService("iss-local", issuerRegistry["iss-local"], auditService, logger)
|
||||
localCAConnector, _ := issuerRegistry.Get("iss-local")
|
||||
estService := service.NewESTService("iss-local", localCAConnector, auditService, logger)
|
||||
estHandler := handler.NewESTHandler(estService)
|
||||
|
||||
// Create router and register handlers
|
||||
@@ -954,6 +954,14 @@ func (m *mockIssuerRepository) Update(ctx context.Context, issuer *domain.Issuer
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockIssuerRepository) CreateIfNotExists(ctx context.Context, issuer *domain.Issuer) (bool, error) {
|
||||
if _, exists := m.issuers[issuer.ID]; exists {
|
||||
return false, nil
|
||||
}
|
||||
m.issuers[issuer.ID] = issuer
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (m *mockIssuerRepository) Delete(ctx context.Context, id string) error {
|
||||
delete(m.issuers, id)
|
||||
return nil
|
||||
|
||||
@@ -36,9 +36,8 @@ func setupTestServer(t *testing.T) (*httptest.Server, *mockCertificateRepository
|
||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
localCA := local.New(nil, logger)
|
||||
|
||||
issuerRegistry := map[string]service.IssuerConnector{
|
||||
"iss-local": service.NewIssuerConnectorAdapter(localCA),
|
||||
}
|
||||
issuerRegistry := service.NewIssuerRegistry(logger)
|
||||
issuerRegistry.Set("iss-local", service.NewIssuerConnectorAdapter(localCA))
|
||||
|
||||
revocationRepo := newMockRevocationRepository()
|
||||
|
||||
@@ -59,7 +58,7 @@ func setupTestServer(t *testing.T) (*httptest.Server, *mockCertificateRepository
|
||||
deploymentService := service.NewDeploymentService(jobRepo, targetRepo, agentRepo, certRepo, auditService, notificationService)
|
||||
jobService := service.NewJobService(jobRepo, renewalService, deploymentService, logger)
|
||||
agentService := service.NewAgentService(agentRepo, certRepo, jobRepo, targetRepo, auditService, issuerRegistry, renewalService)
|
||||
issuerService := service.NewIssuerService(issuerRepo, auditService)
|
||||
issuerService := service.NewIssuerService(issuerRepo, auditService, issuerRegistry, nil, logger)
|
||||
|
||||
certificateHandler := handler.NewCertificateHandler(certificateService)
|
||||
issuerHandler := handler.NewIssuerHandler(issuerService)
|
||||
@@ -81,7 +80,8 @@ func setupTestServer(t *testing.T) (*httptest.Server, *mockCertificateRepository
|
||||
verificationHandler := handler.NewVerificationHandler(&mockVerificationService{})
|
||||
|
||||
// EST handler — uses real Local CA issuer via ESTService
|
||||
estService := service.NewESTService("iss-local", issuerRegistry["iss-local"], auditService, logger)
|
||||
localCAConnector, _ := issuerRegistry.Get("iss-local")
|
||||
estService := service.NewESTService("iss-local", localCAConnector, auditService, logger)
|
||||
estHandler := handler.NewESTHandler(estService)
|
||||
|
||||
r := router.New()
|
||||
|
||||
Reference in New Issue
Block a user