mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 19:41:30 +00:00
fix: replace fmt.Printf with structured slog logging across all services
All 10 service files now use slog.Error for failure logging instead of fmt.Printf. Audit event recording errors are checked and logged rather than silently discarded. Adds consistent structured context (resource IDs, operation names) to all error log statements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+15
-10
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
@@ -73,7 +74,7 @@ func (s *AgentService) Register(ctx context.Context, name string, hostname strin
|
||||
if err := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"agent_registered", "agent", agent.ID,
|
||||
map[string]interface{}{"name": name, "hostname": hostname}); err != nil {
|
||||
fmt.Printf("failed to record audit event: %v\n", err)
|
||||
slog.Error("failed to record audit event", "error", err)
|
||||
}
|
||||
|
||||
// Return the API key only once; the agent must save it securely
|
||||
@@ -96,7 +97,7 @@ func (s *AgentService) HeartbeatWithContext(ctx context.Context, agentID string)
|
||||
if agent.Status != domain.AgentStatusOnline {
|
||||
agent.Status = domain.AgentStatusOnline
|
||||
if err := s.agentRepo.Update(ctx, agent); err != nil {
|
||||
fmt.Printf("failed to update agent status: %v\n", err)
|
||||
slog.Error("failed to update agent status", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,13 +141,15 @@ func (s *AgentService) SubmitCSR(ctx context.Context, agentID string, certID str
|
||||
}
|
||||
|
||||
// Record audit event
|
||||
_ = s.auditService.RecordEvent(ctx, agent.ID, domain.ActorTypeAgent,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, agent.ID, domain.ActorTypeAgent,
|
||||
"csr_submitted", "certificate", certID,
|
||||
map[string]interface{}{
|
||||
"agent_hostname": agent.Hostname,
|
||||
"keygen_mode": "agent",
|
||||
"job_id": awaitingJobs[0].ID,
|
||||
})
|
||||
}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -181,15 +184,17 @@ func (s *AgentService) SubmitCSR(ctx context.Context, agentID string, certID str
|
||||
cert.LastRenewalAt = &now
|
||||
cert.UpdatedAt = now
|
||||
if err := s.certRepo.Update(ctx, cert); err != nil {
|
||||
fmt.Printf("failed to update certificate: %v\n", err)
|
||||
slog.Error("failed to update certificate", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Record audit event
|
||||
_ = s.auditService.RecordEvent(ctx, agent.ID, domain.ActorTypeAgent,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, agent.ID, domain.ActorTypeAgent,
|
||||
"csr_submitted", "certificate", certID,
|
||||
map[string]interface{}{"agent_hostname": agent.Hostname})
|
||||
map[string]interface{}{"agent_hostname": agent.Hostname}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -224,7 +229,7 @@ func (s *AgentService) GetCertificateForAgent(ctx context.Context, agentID strin
|
||||
if err := s.auditService.RecordEvent(ctx, agentID, domain.ActorTypeAgent,
|
||||
"certificate_retrieved", "certificate", certID,
|
||||
map[string]interface{}{"version": latestVersion.SerialNumber}); err != nil {
|
||||
fmt.Printf("failed to record audit event: %v\n", err)
|
||||
slog.Error("failed to record audit event", "error", err)
|
||||
}
|
||||
|
||||
return []byte(latestVersion.PEMChain), nil
|
||||
@@ -283,7 +288,7 @@ func (s *AgentService) ReportJobStatus(ctx context.Context, agentID string, jobI
|
||||
if err := s.auditService.RecordEvent(ctx, agentID, domain.ActorTypeAgent,
|
||||
"job_status_reported", "job", jobID,
|
||||
map[string]interface{}{"status": status, "error": errMsg}); err != nil {
|
||||
fmt.Printf("failed to record audit event: %v\n", err)
|
||||
slog.Error("failed to record audit event", "error", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -302,7 +307,7 @@ func (s *AgentService) MarkStaleAgentsOffline(ctx context.Context, threshold tim
|
||||
if agent.Status == domain.AgentStatusOnline && agent.LastHeartbeatAt != nil && agent.LastHeartbeatAt.Before(cutoff) {
|
||||
agent.Status = domain.AgentStatusOffline
|
||||
if err := s.agentRepo.Update(ctx, agent); err != nil {
|
||||
fmt.Printf("failed to mark agent %s offline: %v\n", agent.ID, err)
|
||||
slog.Error("failed to mark agent offline", "agent_id", agent.ID, "error", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/shankar0123/certctl/internal/domain"
|
||||
@@ -62,9 +63,11 @@ func (s *CertificateService) Create(ctx context.Context, cert *domain.ManagedCer
|
||||
if len(violations) > 0 {
|
||||
// Record violations but do not block creation
|
||||
for _, v := range violations {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
|
||||
"policy_violation_detected", "certificate", cert.ID,
|
||||
map[string]interface{}{"rule_id": v.RuleID, "message": v.Message})
|
||||
map[string]interface{}{"rule_id": v.RuleID, "message": v.Message}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +81,7 @@ func (s *CertificateService) Create(ctx context.Context, cert *domain.ManagedCer
|
||||
"certificate_created", "certificate", cert.ID,
|
||||
map[string]interface{}{"common_name": cert.CommonName}); err != nil {
|
||||
// Log but don't fail the operation
|
||||
fmt.Printf("failed to record audit event: %v\n", err)
|
||||
slog.Error("failed to record audit event", "error", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -98,9 +101,11 @@ func (s *CertificateService) Update(ctx context.Context, cert *domain.ManagedCer
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
for _, v := range violations {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
|
||||
"policy_violation_detected", "certificate", cert.ID,
|
||||
map[string]interface{}{"rule_id": v.RuleID, "message": v.Message})
|
||||
map[string]interface{}{"rule_id": v.RuleID, "message": v.Message}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +125,7 @@ func (s *CertificateService) Update(ctx context.Context, cert *domain.ManagedCer
|
||||
|
||||
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
|
||||
"certificate_updated", "certificate", cert.ID, changes); err != nil {
|
||||
fmt.Printf("failed to record audit event: %v\n", err)
|
||||
slog.Error("failed to record audit event", "error", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -140,7 +145,7 @@ func (s *CertificateService) Archive(ctx context.Context, id string, actor strin
|
||||
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
|
||||
"certificate_archived", "certificate", id,
|
||||
map[string]interface{}{"common_name": cert.CommonName}); err != nil {
|
||||
fmt.Printf("failed to record audit event: %v\n", err)
|
||||
slog.Error("failed to record audit event", "error", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -185,7 +190,7 @@ func (s *CertificateService) TriggerRenewalWithActor(ctx context.Context, certID
|
||||
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
|
||||
"renewal_triggered", "certificate", certID,
|
||||
map[string]interface{}{"common_name": cert.CommonName}); err != nil {
|
||||
fmt.Printf("failed to record audit event: %v\n", err)
|
||||
slog.Error("failed to record audit event", "error", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -207,7 +212,7 @@ func (s *CertificateService) TriggerDeploymentWithActor(ctx context.Context, cer
|
||||
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
|
||||
"deployment_triggered", "certificate", certID,
|
||||
map[string]interface{}{"common_name": cert.CommonName}); err != nil {
|
||||
fmt.Printf("failed to record audit event: %v\n", err)
|
||||
slog.Error("failed to record audit event", "error", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/shankar0123/certctl/internal/domain"
|
||||
@@ -68,7 +69,7 @@ func (s *DeploymentService) CreateDeploymentJobs(ctx context.Context, certID str
|
||||
}
|
||||
|
||||
if err := s.jobRepo.Create(ctx, job); err != nil {
|
||||
fmt.Printf("failed to create deployment job for target %s: %v\n", target.ID, err)
|
||||
slog.Error("failed to create deployment job for target", "target_id", target.ID, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -80,9 +81,11 @@ func (s *DeploymentService) CreateDeploymentJobs(ctx context.Context, certID str
|
||||
}
|
||||
|
||||
// Record audit event
|
||||
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"deployment_jobs_created", "certificate", certID,
|
||||
map[string]interface{}{"target_count": len(targets), "job_count": len(jobIDs)})
|
||||
map[string]interface{}{"target_count": len(targets), "job_count": len(jobIDs)}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
|
||||
return jobIDs, nil
|
||||
}
|
||||
@@ -99,7 +102,7 @@ func (s *DeploymentService) ProcessDeploymentJob(ctx context.Context, job *domai
|
||||
if err != nil {
|
||||
updateErr := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusFailed, fmt.Sprintf("certificate fetch failed: %v", err))
|
||||
if updateErr != nil {
|
||||
fmt.Printf("failed to update job status: %v\n", updateErr)
|
||||
slog.Error("failed to update job status", "job_id", job.ID, "error", updateErr)
|
||||
}
|
||||
return fmt.Errorf("failed to fetch certificate: %w", err)
|
||||
}
|
||||
@@ -112,7 +115,7 @@ func (s *DeploymentService) ProcessDeploymentJob(ctx context.Context, job *domai
|
||||
if targetID == "" {
|
||||
updateErr := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusFailed, "target_id not found in job")
|
||||
if updateErr != nil {
|
||||
fmt.Printf("failed to update job status: %v\n", updateErr)
|
||||
slog.Error("failed to update job status", "job_id", job.ID, "error", updateErr)
|
||||
}
|
||||
return fmt.Errorf("target_id not found in job")
|
||||
}
|
||||
@@ -121,7 +124,7 @@ func (s *DeploymentService) ProcessDeploymentJob(ctx context.Context, job *domai
|
||||
if err != nil {
|
||||
updateErr := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusFailed, fmt.Sprintf("target fetch failed: %v", err))
|
||||
if updateErr != nil {
|
||||
fmt.Printf("failed to update job status: %v\n", updateErr)
|
||||
slog.Error("failed to update job status", "job_id", job.ID, "error", updateErr)
|
||||
}
|
||||
return fmt.Errorf("failed to fetch target: %w", err)
|
||||
}
|
||||
@@ -132,7 +135,7 @@ func (s *DeploymentService) ProcessDeploymentJob(ctx context.Context, job *domai
|
||||
if err != nil {
|
||||
updateErr := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusFailed, fmt.Sprintf("agent fetch failed: %v", err))
|
||||
if updateErr != nil {
|
||||
fmt.Printf("failed to update job status: %v\n", updateErr)
|
||||
slog.Error("failed to update job status", "job_id", job.ID, "error", updateErr)
|
||||
}
|
||||
return fmt.Errorf("failed to fetch agent: %w", err)
|
||||
}
|
||||
@@ -141,13 +144,17 @@ func (s *DeploymentService) ProcessDeploymentJob(ctx context.Context, job *domai
|
||||
if agent.LastHeartbeatAt != nil && time.Since(*agent.LastHeartbeatAt) > 5*time.Minute {
|
||||
updateErr := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusFailed, "agent is offline")
|
||||
if updateErr != nil {
|
||||
fmt.Printf("failed to update job status: %v\n", updateErr)
|
||||
slog.Error("failed to update job status", "job_id", job.ID, "error", updateErr)
|
||||
}
|
||||
|
||||
_ = s.notificationSvc.SendDeploymentNotification(ctx, cert, target, false, fmt.Errorf("agent offline"))
|
||||
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
if notifErr := s.notificationSvc.SendDeploymentNotification(ctx, cert, target, false, fmt.Errorf("agent offline")); notifErr != nil {
|
||||
slog.Error("failed to send deployment notification", "error", notifErr)
|
||||
}
|
||||
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"deployment_job_failed", "certificate", job.CertificateID,
|
||||
map[string]interface{}{"job_id": job.ID, "reason": "agent offline", "target_id": targetID})
|
||||
map[string]interface{}{"job_id": job.ID, "reason": "agent offline", "target_id": targetID}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
|
||||
return fmt.Errorf("agent %s is offline", agentID)
|
||||
}
|
||||
@@ -157,9 +164,11 @@ func (s *DeploymentService) ProcessDeploymentJob(ctx context.Context, job *domai
|
||||
// For now, we mark it as pending and rely on agent polling.
|
||||
|
||||
// Record audit event
|
||||
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"deployment_job_dispatched", "certificate", job.CertificateID,
|
||||
map[string]interface{}{"job_id": job.ID, "target_id": targetID, "agent_id": agentID})
|
||||
map[string]interface{}{"job_id": job.ID, "target_id": targetID, "agent_id": agentID}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -217,7 +226,7 @@ func (s *DeploymentService) MarkDeploymentComplete(ctx context.Context, jobID st
|
||||
// Fetch certificate and target for notification
|
||||
cert, err := s.certRepo.Get(ctx, job.CertificateID)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to fetch certificate for notification: %v\n", err)
|
||||
slog.Error("failed to fetch certificate for notification", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -229,20 +238,22 @@ func (s *DeploymentService) MarkDeploymentComplete(ctx context.Context, jobID st
|
||||
if targetID != "" {
|
||||
target, err := s.targetRepo.Get(ctx, targetID)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to fetch target for notification: %v\n", err)
|
||||
slog.Error("failed to fetch target for notification", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send deployment success notification
|
||||
if err := s.notificationSvc.SendDeploymentNotification(ctx, cert, target, true, nil); err != nil {
|
||||
fmt.Printf("failed to send deployment notification: %v\n", err)
|
||||
slog.Error("failed to send deployment notification", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Record audit event
|
||||
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"deployment_job_completed", "certificate", job.CertificateID,
|
||||
map[string]interface{}{"job_id": jobID, "target_id": targetID})
|
||||
map[string]interface{}{"job_id": jobID, "target_id": targetID}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -262,7 +273,7 @@ func (s *DeploymentService) MarkDeploymentFailed(ctx context.Context, jobID stri
|
||||
// Fetch certificate and target for notification
|
||||
cert, err := s.certRepo.Get(ctx, job.CertificateID)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to fetch certificate for notification: %v\n", err)
|
||||
slog.Error("failed to fetch certificate for notification", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -274,20 +285,22 @@ func (s *DeploymentService) MarkDeploymentFailed(ctx context.Context, jobID stri
|
||||
if targetID != "" {
|
||||
target, err := s.targetRepo.Get(ctx, targetID)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to fetch target for notification: %v\n", err)
|
||||
slog.Error("failed to fetch target for notification", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send deployment failure notification
|
||||
if err := s.notificationSvc.SendDeploymentNotification(ctx, cert, target, false, fmt.Errorf("%s", errMsg)); err != nil {
|
||||
fmt.Printf("failed to send deployment notification: %v\n", err)
|
||||
slog.Error("failed to send deployment notification", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Record audit event
|
||||
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"deployment_job_failed", "certificate", job.CertificateID,
|
||||
map[string]interface{}{"job_id": jobID, "target_id": targetID, "error": errMsg})
|
||||
map[string]interface{}{"job_id": jobID, "target_id": targetID, "error": errMsg}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/shankar0123/certctl/internal/domain"
|
||||
@@ -81,7 +82,9 @@ func (s *IssuerService) Create(ctx context.Context, issuer *domain.Issuer, actor
|
||||
}
|
||||
|
||||
if s.auditService != nil {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_issuer", "issuer", issuer.ID, nil)
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_issuer", "issuer", issuer.ID, nil); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -99,7 +102,9 @@ func (s *IssuerService) Update(ctx context.Context, id string, issuer *domain.Is
|
||||
}
|
||||
|
||||
if s.auditService != nil {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_issuer", "issuer", id, nil)
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_issuer", "issuer", id, nil); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -112,7 +117,9 @@ func (s *IssuerService) Delete(ctx context.Context, id string, actor string) err
|
||||
}
|
||||
|
||||
if s.auditService != nil {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_issuer", "issuer", id, nil)
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_issuer", "issuer", id, nil); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/shankar0123/certctl/internal/domain"
|
||||
@@ -188,7 +189,7 @@ func (s *NotificationService) ProcessPendingNotifications(ctx context.Context) e
|
||||
|
||||
for _, notif := range pending {
|
||||
if err := s.sendNotification(ctx, notif); err != nil {
|
||||
fmt.Printf("failed to send notification %s: %v\n", notif.ID, err)
|
||||
slog.Error("failed to send notification", "notification_id", notif.ID, "error", err)
|
||||
failedCount++
|
||||
}
|
||||
}
|
||||
@@ -206,20 +207,24 @@ func (s *NotificationService) sendNotification(ctx context.Context, notif *domai
|
||||
notifier, ok := s.notifierRegistry[string(notif.Channel)]
|
||||
if !ok {
|
||||
// No notifier configured for this channel — mark as sent (demo mode)
|
||||
_ = s.notifRepo.UpdateStatus(ctx, notif.ID, "sent", time.Now())
|
||||
if updateErr := s.notifRepo.UpdateStatus(ctx, notif.ID, "sent", time.Now()); updateErr != nil {
|
||||
slog.Error("failed to update notification status", "notification_id", notif.ID, "error", updateErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send the notification
|
||||
if err := notifier.Send(ctx, notif.Recipient, string(notif.Type), notif.Message); err != nil {
|
||||
// Update status to failed
|
||||
_ = s.notifRepo.UpdateStatus(ctx, notif.ID, "failed", time.Time{})
|
||||
if updateErr := s.notifRepo.UpdateStatus(ctx, notif.ID, "failed", time.Time{}); updateErr != nil {
|
||||
slog.Error("failed to update notification status", "notification_id", notif.ID, "error", updateErr)
|
||||
}
|
||||
return fmt.Errorf("failed to send via %s: %w", notif.Channel, err)
|
||||
}
|
||||
|
||||
// Update status to sent
|
||||
if err := s.notifRepo.UpdateStatus(ctx, notif.ID, "sent", time.Now()); err != nil {
|
||||
fmt.Printf("failed to update notification status: %v\n", err)
|
||||
slog.Error("failed to update notification status", "notification_id", notif.ID, "error", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/shankar0123/certctl/internal/domain"
|
||||
@@ -81,7 +82,9 @@ func (s *OwnerService) Create(ctx context.Context, owner *domain.Owner, actor st
|
||||
}
|
||||
|
||||
if s.auditService != nil {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_owner", "owner", owner.ID, nil)
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_owner", "owner", owner.ID, nil); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -99,7 +102,9 @@ func (s *OwnerService) Update(ctx context.Context, id string, owner *domain.Owne
|
||||
}
|
||||
|
||||
if s.auditService != nil {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_owner", "owner", id, nil)
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_owner", "owner", id, nil); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -112,7 +117,9 @@ func (s *OwnerService) Delete(ctx context.Context, id string, actor string) erro
|
||||
}
|
||||
|
||||
if s.auditService != nil {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_owner", "owner", id, nil)
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_owner", "owner", id, nil); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/shankar0123/certctl/internal/domain"
|
||||
@@ -44,7 +45,7 @@ func (s *PolicyService) ValidateCertificate(ctx context.Context, cert *domain.Ma
|
||||
// Evaluate rule against certificate
|
||||
v, err := s.evaluateRule(rule, cert)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to evaluate rule %s: %v\n", rule.ID, err)
|
||||
slog.Error("failed to evaluate rule", "rule_id", rule.ID, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -149,7 +150,7 @@ func (s *PolicyService) CreateRule(ctx context.Context, rule *domain.PolicyRule,
|
||||
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
|
||||
"policy_rule_created", "policy", rule.ID,
|
||||
map[string]interface{}{"rule_type": rule.Type}); err != nil {
|
||||
fmt.Printf("failed to record audit event: %v\n", err)
|
||||
slog.Error("failed to record audit event", "error", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -175,7 +176,7 @@ func (s *PolicyService) UpdateRule(ctx context.Context, rule *domain.PolicyRule,
|
||||
|
||||
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
|
||||
"policy_rule_updated", "policy", rule.ID, changes); err != nil {
|
||||
fmt.Printf("failed to record audit event: %v\n", err)
|
||||
slog.Error("failed to record audit event", "error", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -213,7 +214,7 @@ func (s *PolicyService) DeleteRule(ctx context.Context, id string, actor string)
|
||||
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
|
||||
"policy_rule_deleted", "policy", id,
|
||||
map[string]interface{}{"rule_type": rule.Type}); err != nil {
|
||||
fmt.Printf("failed to record audit event: %v\n", err)
|
||||
slog.Error("failed to record audit event", "error", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+47
-31
@@ -10,6 +10,7 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/shankar0123/certctl/internal/domain"
|
||||
@@ -103,8 +104,7 @@ func (s *RenewalService) CheckExpiringCertificates(ctx context.Context) error {
|
||||
policy, err = s.renewalPolicyRepo.Get(ctx, cert.RenewalPolicyID)
|
||||
if err != nil {
|
||||
// Log but continue with defaults
|
||||
fmt.Printf("failed to fetch renewal policy %s for cert %s, using defaults: %v\n",
|
||||
cert.RenewalPolicyID, cert.ID, err)
|
||||
slog.Error("failed to fetch renewal policy, using defaults", "policy_id", cert.RenewalPolicyID, "cert_id", cert.ID, "error", err)
|
||||
} else {
|
||||
policyCache[cert.RenewalPolicyID] = policy
|
||||
}
|
||||
@@ -153,20 +153,22 @@ func (s *RenewalService) CheckExpiringCertificates(ctx context.Context) error {
|
||||
}
|
||||
|
||||
if err := s.jobRepo.Create(ctx, job); err != nil {
|
||||
fmt.Printf("failed to create renewal job for cert %s: %v\n", cert.ID, err)
|
||||
slog.Error("failed to create renewal job for cert", "cert_id", cert.ID, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Update certificate status to RenewalInProgress
|
||||
cert.Status = domain.CertificateStatusRenewalInProgress
|
||||
if err := s.certRepo.Update(ctx, cert); err != nil {
|
||||
fmt.Printf("failed to update cert status for %s: %v\n", cert.ID, err)
|
||||
slog.Error("failed to update cert status", "cert_id", cert.ID, "error", err)
|
||||
}
|
||||
|
||||
// Record audit event
|
||||
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"renewal_job_created", "certificate", cert.ID,
|
||||
map[string]interface{}{"days_until_expiry": daysUntil, "job_id": job.ID})
|
||||
map[string]interface{}{"days_until_expiry": daysUntil, "job_id": job.ID}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -186,8 +188,7 @@ func (s *RenewalService) sendThresholdAlerts(ctx context.Context, cert *domain.M
|
||||
// Check if we already sent a notification for this threshold (deduplication)
|
||||
alreadySent, err := s.notificationSvc.HasThresholdNotification(ctx, cert.ID, threshold)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to check notification dedup for cert %s threshold %d: %v\n",
|
||||
cert.ID, threshold, err)
|
||||
slog.Error("failed to check notification dedup", "cert_id", cert.ID, "threshold", threshold, "error", err)
|
||||
continue
|
||||
}
|
||||
if alreadySent {
|
||||
@@ -196,17 +197,18 @@ func (s *RenewalService) sendThresholdAlerts(ctx context.Context, cert *domain.M
|
||||
|
||||
// Send the threshold alert
|
||||
if err := s.notificationSvc.SendThresholdAlert(ctx, cert, daysUntil, threshold); err != nil {
|
||||
fmt.Printf("failed to send threshold alert for cert %s at %d days: %v\n",
|
||||
cert.ID, threshold, err)
|
||||
slog.Error("failed to send threshold alert for cert", "cert_id", cert.ID, "threshold", threshold, "error", err)
|
||||
}
|
||||
|
||||
// Record audit event for the alert
|
||||
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"expiration_alert_sent", "certificate", cert.ID,
|
||||
map[string]interface{}{
|
||||
"threshold_days": threshold,
|
||||
"days_until_expiry": daysUntil,
|
||||
})
|
||||
}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +236,7 @@ func (s *RenewalService) updateCertExpiryStatus(ctx context.Context, cert *domai
|
||||
cert.Status = newStatus
|
||||
cert.UpdatedAt = time.Now()
|
||||
if err := s.certRepo.Update(ctx, cert); err != nil {
|
||||
fmt.Printf("failed to update cert %s status to %s: %v\n", cert.ID, newStatus, err)
|
||||
slog.Error("failed to update cert status", "cert_id", cert.ID, "new_status", newStatus, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,13 +292,15 @@ func (s *RenewalService) processRenewalAgentKeygen(ctx context.Context, job *dom
|
||||
cert.Status = domain.CertificateStatusRenewalInProgress
|
||||
cert.UpdatedAt = time.Now()
|
||||
if err := s.certRepo.Update(ctx, cert); err != nil {
|
||||
fmt.Printf("failed to update cert status for %s: %v\n", cert.ID, err)
|
||||
slog.Error("failed to update cert status", "cert_id", cert.ID, "error", err)
|
||||
}
|
||||
|
||||
// Record audit event
|
||||
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"renewal_awaiting_csr", "certificate", job.CertificateID,
|
||||
map[string]interface{}{"job_id": job.ID, "keygen_mode": "agent"})
|
||||
map[string]interface{}{"job_id": job.ID, "keygen_mode": "agent"}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -343,10 +347,14 @@ func (s *RenewalService) processRenewalServerKeygen(ctx context.Context, job *do
|
||||
result, err := connector.RenewCertificate(ctx, cert.CommonName, cert.SANs, csrPEM)
|
||||
if err != nil {
|
||||
s.failJob(ctx, job, fmt.Sprintf("issuer renewal failed: %v", err))
|
||||
_ = s.notificationSvc.SendRenewalNotification(ctx, cert, false, err)
|
||||
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
if notifErr := s.notificationSvc.SendRenewalNotification(ctx, cert, false, err); notifErr != nil {
|
||||
slog.Error("failed to send renewal failure notification", "error", notifErr)
|
||||
}
|
||||
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"renewal_job_failed", "certificate", job.CertificateID,
|
||||
map[string]interface{}{"job_id": job.ID, "error": err.Error()})
|
||||
map[string]interface{}{"job_id": job.ID, "error": err.Error()}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
return fmt.Errorf("issuer renewal failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -392,18 +400,20 @@ func (s *RenewalService) processRenewalServerKeygen(ctx context.Context, job *do
|
||||
|
||||
// Send success notification
|
||||
if err := s.notificationSvc.SendRenewalNotification(ctx, cert, true, nil); err != nil {
|
||||
fmt.Printf("failed to send renewal notification: %v\n", err)
|
||||
slog.Error("failed to send renewal notification", "error", err)
|
||||
}
|
||||
|
||||
// Record audit event
|
||||
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"renewal_job_completed", "certificate", job.CertificateID,
|
||||
map[string]interface{}{
|
||||
"job_id": job.ID,
|
||||
"serial": result.Serial,
|
||||
"not_after": result.NotAfter,
|
||||
"keygen_mode": "server",
|
||||
})
|
||||
}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -427,10 +437,14 @@ func (s *RenewalService) CompleteAgentCSRRenewal(ctx context.Context, job *domai
|
||||
result, err := connector.RenewCertificate(ctx, cert.CommonName, cert.SANs, csrPEM)
|
||||
if err != nil {
|
||||
s.failJob(ctx, job, fmt.Sprintf("issuer signing failed: %v", err))
|
||||
_ = s.notificationSvc.SendRenewalNotification(ctx, cert, false, err)
|
||||
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
if notifErr := s.notificationSvc.SendRenewalNotification(ctx, cert, false, err); notifErr != nil {
|
||||
slog.Error("failed to send renewal failure notification", "error", notifErr)
|
||||
}
|
||||
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"renewal_job_failed", "certificate", job.CertificateID,
|
||||
map[string]interface{}{"job_id": job.ID, "error": err.Error()})
|
||||
map[string]interface{}{"job_id": job.ID, "error": err.Error()}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
return fmt.Errorf("issuer signing failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -475,18 +489,20 @@ func (s *RenewalService) CompleteAgentCSRRenewal(ctx context.Context, job *domai
|
||||
|
||||
// Send success notification
|
||||
if err := s.notificationSvc.SendRenewalNotification(ctx, cert, true, nil); err != nil {
|
||||
fmt.Printf("failed to send renewal notification: %v\n", err)
|
||||
slog.Error("failed to send renewal notification", "error", err)
|
||||
}
|
||||
|
||||
// Record audit event
|
||||
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
|
||||
"renewal_job_completed", "certificate", cert.ID,
|
||||
map[string]interface{}{
|
||||
"job_id": job.ID,
|
||||
"serial": result.Serial,
|
||||
"not_after": result.NotAfter,
|
||||
"keygen_mode": "agent",
|
||||
})
|
||||
}); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -509,7 +525,7 @@ func (s *RenewalService) createDeploymentJobs(ctx context.Context, cert *domain.
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if err := s.jobRepo.Create(ctx, deployJob); err != nil {
|
||||
fmt.Printf("failed to create deployment job for target %s: %v\n", targetID, err)
|
||||
slog.Error("failed to create deployment job for target", "target_id", targetID, "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -532,7 +548,7 @@ func (s *RenewalService) GetAwaitingCSRJobs(ctx context.Context, certID string)
|
||||
// failJob is a helper to mark a job as failed with an error message.
|
||||
func (s *RenewalService) failJob(ctx context.Context, job *domain.Job, errMsg string) {
|
||||
if updateErr := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusFailed, errMsg); updateErr != nil {
|
||||
fmt.Printf("failed to update job status: %v\n", updateErr)
|
||||
slog.Error("failed to update job status", "job_id", job.ID, "error", updateErr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,7 +581,7 @@ func (s *RenewalService) RetryFailedJobs(ctx context.Context, maxRetries int) er
|
||||
|
||||
// Reset status to pending for retry
|
||||
if err := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusPending, ""); err != nil {
|
||||
fmt.Printf("failed to reset job status for retry: %v\n", err)
|
||||
slog.Error("failed to reset job status for retry", "job_id", job.ID, "error", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/shankar0123/certctl/internal/domain"
|
||||
@@ -81,7 +82,9 @@ func (s *TargetService) Create(ctx context.Context, target *domain.DeploymentTar
|
||||
}
|
||||
|
||||
if s.auditService != nil {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_target", "target", target.ID, nil)
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_target", "target", target.ID, nil); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -99,7 +102,9 @@ func (s *TargetService) Update(ctx context.Context, id string, target *domain.De
|
||||
}
|
||||
|
||||
if s.auditService != nil {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_target", "target", id, nil)
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_target", "target", id, nil); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -112,7 +117,9 @@ func (s *TargetService) Delete(ctx context.Context, id string, actor string) err
|
||||
}
|
||||
|
||||
if s.auditService != nil {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_target", "target", id, nil)
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_target", "target", id, nil); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/shankar0123/certctl/internal/domain"
|
||||
@@ -81,7 +82,9 @@ func (s *TeamService) Create(ctx context.Context, team *domain.Team, actor strin
|
||||
}
|
||||
|
||||
if s.auditService != nil {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_team", "team", team.ID, nil)
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_team", "team", team.ID, nil); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -99,7 +102,9 @@ func (s *TeamService) Update(ctx context.Context, id string, team *domain.Team,
|
||||
}
|
||||
|
||||
if s.auditService != nil {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_team", "team", id, nil)
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_team", "team", id, nil); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -112,7 +117,9 @@ func (s *TeamService) Delete(ctx context.Context, id string, actor string) error
|
||||
}
|
||||
|
||||
if s.auditService != nil {
|
||||
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_team", "team", id, nil)
|
||||
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_team", "team", id, nil); auditErr != nil {
|
||||
slog.Error("failed to record audit event", "error", auditErr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user