diff --git a/internal/api/handler/job_handler_test.go b/internal/api/handler/job_handler_test.go index b8ea556..15f93a5 100644 --- a/internal/api/handler/job_handler_test.go +++ b/internal/api/handler/job_handler_test.go @@ -12,8 +12,8 @@ import ( // MockJobService is a mock implementation of JobService interface. type MockJobService struct { - ListJobsFn func(status, jobType string, page, perPage int) ([]domain.Job, int64, error) - GetJobFn func(id string) (*domain.Job, error) + ListJobsFn func(status, jobType string, page, perPage int) ([]domain.Job, int64, error) + GetJobFn func(id string) (*domain.Job, error) CancelJobFn func(id string) error } diff --git a/internal/api/handler/validation.go b/internal/api/handler/validation.go index 5c1364f..bbda58d 100644 --- a/internal/api/handler/validation.go +++ b/internal/api/handler/validation.go @@ -126,7 +126,7 @@ func isValidHostnameChar(r rune) bool { r == '.' || r == '-' || r == '_' || // Underscores are sometimes allowed - r == '*' // Wildcard support + r == '*' // Wildcard support } // Error method makes ValidationError satisfy the error interface. diff --git a/internal/config/config.go b/internal/config/config.go index a66bf2a..ccf75c7 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -37,9 +37,9 @@ type ServerConfig struct { // DatabaseConfig contains database connection configuration. type DatabaseConfig struct { - URL string - MaxConnections int - MigrationsPath string + URL string + MaxConnections int + MigrationsPath string } // SchedulerConfig contains scheduler timing configuration. @@ -64,9 +64,9 @@ type AuthConfig struct { // RateLimitConfig contains rate limiting configuration. type RateLimitConfig struct { - Enabled bool - RPS float64 // Requests per second - BurstSize int // Maximum burst size + Enabled bool + RPS float64 // Requests per second + BurstSize int // Maximum burst size } // CORSConfig contains CORS configuration. diff --git a/internal/connector/issuer/acme/acme.go b/internal/connector/issuer/acme/acme.go index 8630f5d..ab44ed5 100644 --- a/internal/connector/issuer/acme/acme.go +++ b/internal/connector/issuer/acme/acme.go @@ -22,10 +22,10 @@ import ( // Config represents the ACME issuer connector configuration. type Config struct { - DirectoryURL string `json:"directory_url"` // ACME directory URL (e.g., https://acme-staging-v02.api.letsencrypt.org/directory) - Email string `json:"email"` // Contact email for the ACME account - EABKid string `json:"eab_kid,omitempty"` // External Account Binding Key ID (for some CAs) - EABHmac string `json:"eab_hmac,omitempty"` // External Account Binding HMAC Key + DirectoryURL string `json:"directory_url"` // ACME directory URL (e.g., https://acme-staging-v02.api.letsencrypt.org/directory) + Email string `json:"email"` // Contact email for the ACME account + EABKid string `json:"eab_kid,omitempty"` // External Account Binding Key ID (for some CAs) + EABHmac string `json:"eab_hmac,omitempty"` // External Account Binding HMAC Key HTTPPort int `json:"http_port,omitempty"` // Port for HTTP-01 challenge server (default: 80) } diff --git a/internal/connector/issuer/interface.go b/internal/connector/issuer/interface.go index 8e6181d..cb4f938 100644 --- a/internal/connector/issuer/interface.go +++ b/internal/connector/issuer/interface.go @@ -33,12 +33,12 @@ type IssuanceRequest struct { // IssuanceResult contains the result of a successful certificate issuance. type IssuanceResult struct { - CertPEM string `json:"cert_pem"` - ChainPEM string `json:"chain_pem"` - Serial string `json:"serial"` - NotBefore time.Time `json:"not_before"` - NotAfter time.Time `json:"not_after"` - OrderID string `json:"order_id"` + CertPEM string `json:"cert_pem"` + ChainPEM string `json:"chain_pem"` + Serial string `json:"serial"` + NotBefore time.Time `json:"not_before"` + NotAfter time.Time `json:"not_after"` + OrderID string `json:"order_id"` } // RenewalRequest contains the parameters for renewing a certificate. @@ -57,13 +57,13 @@ type RevocationRequest struct { // OrderStatus contains the status of a pending issuance or renewal order. type OrderStatus struct { - OrderID string `json:"order_id"` - Status string `json:"status"` - Message *string `json:"message,omitempty"` - CertPEM *string `json:"cert_pem,omitempty"` - ChainPEM *string `json:"chain_pem,omitempty"` - Serial *string `json:"serial,omitempty"` - NotBefore *time.Time `json:"not_before,omitempty"` - NotAfter *time.Time `json:"not_after,omitempty"` - UpdatedAt time.Time `json:"updated_at"` + OrderID string `json:"order_id"` + Status string `json:"status"` + Message *string `json:"message,omitempty"` + CertPEM *string `json:"cert_pem,omitempty"` + ChainPEM *string `json:"chain_pem,omitempty"` + Serial *string `json:"serial,omitempty"` + NotBefore *time.Time `json:"not_before,omitempty"` + NotAfter *time.Time `json:"not_after,omitempty"` + UpdatedAt time.Time `json:"updated_at"` } diff --git a/internal/connector/issuer/local/local.go b/internal/connector/issuer/local/local.go index 19df541..ab0af08 100644 --- a/internal/connector/issuer/local/local.go +++ b/internal/connector/issuer/local/local.go @@ -379,10 +379,10 @@ func (c *Connector) generateCertificate(csr *x509.CertificateRequest, additional x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth, }, - DNSNames: dnsNames, - EmailAddresses: emails, - SubjectKeyId: hashPublicKey(csr.PublicKey), - AuthorityKeyId: c.caCert.SubjectKeyId, + DNSNames: dnsNames, + EmailAddresses: emails, + SubjectKeyId: hashPublicKey(csr.PublicKey), + AuthorityKeyId: c.caCert.SubjectKeyId, } // Add IP addresses if present diff --git a/internal/connector/notifier/interface.go b/internal/connector/notifier/interface.go index 827a107..9855f55 100644 --- a/internal/connector/notifier/interface.go +++ b/internal/connector/notifier/interface.go @@ -20,14 +20,14 @@ type Connector interface { // Alert represents a notification alert with urgency. type Alert struct { - ID string `json:"id"` - Type string `json:"type"` - Severity string `json:"severity"` - Subject string `json:"subject"` - Message string `json:"message"` - Recipient string `json:"recipient"` - Metadata map[string]string `json:"metadata,omitempty"` - CreatedAt time.Time `json:"created_at"` + ID string `json:"id"` + Type string `json:"type"` + Severity string `json:"severity"` + Subject string `json:"subject"` + Message string `json:"message"` + Recipient string `json:"recipient"` + Metadata map[string]string `json:"metadata,omitempty"` + CreatedAt time.Time `json:"created_at"` } // Event represents a notification event with contextual information. diff --git a/internal/connector/notifier/webhook/webhook.go b/internal/connector/notifier/webhook/webhook.go index 143df2d..305df87 100644 --- a/internal/connector/notifier/webhook/webhook.go +++ b/internal/connector/notifier/webhook/webhook.go @@ -89,14 +89,14 @@ func (c *Connector) SendAlert(ctx context.Context, alert notifier.Alert) error { // Format payload payload := map[string]interface{}{ - "type": "alert", - "alert_id": alert.ID, - "severity": alert.Severity, - "subject": alert.Subject, - "message": alert.Message, - "recipient": alert.Recipient, + "type": "alert", + "alert_id": alert.ID, + "severity": alert.Severity, + "subject": alert.Subject, + "message": alert.Message, + "recipient": alert.Recipient, "created_at": alert.CreatedAt, - "metadata": alert.Metadata, + "metadata": alert.Metadata, } if err := c.postWebhook(ctx, payload); err != nil { diff --git a/internal/connector/target/f5/f5.go b/internal/connector/target/f5/f5.go index a24d35c..72b038d 100644 --- a/internal/connector/target/f5/f5.go +++ b/internal/connector/target/f5/f5.go @@ -13,12 +13,12 @@ import ( // Config represents the F5 BIG-IP deployment target configuration. type Config struct { - Host string `json:"host"` // F5 BIG-IP hostname or IP - Port int `json:"port"` // F5 iControl REST API port (default 443) - Username string `json:"username"` // Administrative username - Password string `json:"password"` // Administrative password - Partition string `json:"partition"` // F5 partition name (e.g., "Common") - SSLProfile string `json:"ssl_profile"` // SSL profile name to update + Host string `json:"host"` // F5 BIG-IP hostname or IP + Port int `json:"port"` // F5 iControl REST API port (default 443) + Username string `json:"username"` // Administrative username + Password string `json:"password"` // Administrative password + Partition string `json:"partition"` // F5 partition name (e.g., "Common") + SSLProfile string `json:"ssl_profile"` // SSL profile name to update } // Connector implements the target.Connector interface for F5 BIG-IP load balancers. @@ -138,10 +138,10 @@ func (c *Connector) DeployCertificate(ctx context.Context, request target.Deploy Message: "Certificate deployment to F5 initiated (stub)", DeployedAt: time.Now(), Metadata: map[string]string{ - "host": c.config.Host, - "partition": c.config.Partition, - "ssl_profile": c.config.SSLProfile, - "duration_ms": fmt.Sprintf("%d", deploymentDuration.Milliseconds()), + "host": c.config.Host, + "partition": c.config.Partition, + "ssl_profile": c.config.SSLProfile, + "duration_ms": fmt.Sprintf("%d", deploymentDuration.Milliseconds()), }, }, nil } diff --git a/internal/connector/target/iis/iis.go b/internal/connector/target/iis/iis.go index 0ae089c..2691535 100644 --- a/internal/connector/target/iis/iis.go +++ b/internal/connector/target/iis/iis.go @@ -14,10 +14,10 @@ import ( // Config represents the IIS deployment target configuration. // This configuration is for Windows agents that manage IIS servers. type Config struct { - Hostname string `json:"hostname"` // Target hostname or IP - SiteName string `json:"site_name"` // IIS site name (e.g., "Default Web Site") - CertStore string `json:"cert_store"` // Windows cert store (e.g., "My", "WebHosting") - BindingInfo string `json:"binding_info"` // Binding info (e.g., "*.example.com") + Hostname string `json:"hostname"` // Target hostname or IP + SiteName string `json:"site_name"` // IIS site name (e.g., "Default Web Site") + CertStore string `json:"cert_store"` // Windows cert store (e.g., "My", "WebHosting") + BindingInfo string `json:"binding_info"` // Binding info (e.g., "*.example.com") } // Connector implements the target.Connector interface for IIS (Internet Information Services). @@ -86,12 +86,12 @@ func (c *Connector) ValidateConfig(ctx context.Context, rawConfig json.RawMessag // the IIS binding to use the new certificate. // // The IIS deployment process (via PowerShell): -// 1. Create a temporary PFX file from the certificate and existing private key -// (Note: The private key is managed by the agent, not provided by the control plane) -// 2. Import the PFX to the Windows certificate store (My store by default) -// 3. Get the certificate thumbprint -// 4. Update the IIS binding to use the new certificate by thumbprint -// 5. Verify the binding is active +// 1. Create a temporary PFX file from the certificate and existing private key +// (Note: The private key is managed by the agent, not provided by the control plane) +// 2. Import the PFX to the Windows certificate store (My store by default) +// 3. Get the certificate thumbprint +// 4. Update the IIS binding to use the new certificate by thumbprint +// 5. Verify the binding is active // // TODO: Implement actual PowerShell commands: // - Import-PfxCertificate -FilePath {pfxPath} -CertStoreLocation "Cert:\LocalMachine\My" @@ -128,10 +128,10 @@ func (c *Connector) DeployCertificate(ctx context.Context, request target.Deploy Message: "Certificate deployment to IIS initiated (stub)", DeployedAt: time.Now(), Metadata: map[string]string{ - "hostname": c.config.Hostname, - "site_name": c.config.SiteName, - "cert_store": c.config.CertStore, - "duration_ms": fmt.Sprintf("%d", deploymentDuration.Milliseconds()), + "hostname": c.config.Hostname, + "site_name": c.config.SiteName, + "cert_store": c.config.CertStore, + "duration_ms": fmt.Sprintf("%d", deploymentDuration.Milliseconds()), }, }, nil } diff --git a/internal/connector/target/interface.go b/internal/connector/target/interface.go index 6a365bd..b17b8f9 100644 --- a/internal/connector/target/interface.go +++ b/internal/connector/target/interface.go @@ -23,37 +23,37 @@ type Connector interface { // In agent keygen mode, KeyPEM is populated from the agent's local key store. // In server keygen mode (demo only), KeyPEM may be empty if the key was embedded in the cert version. type DeploymentRequest struct { - CertPEM string `json:"cert_pem"` - KeyPEM string `json:"key_pem,omitempty"` - ChainPEM string `json:"chain_pem"` - TargetConfig json.RawMessage `json:"target_config"` - Metadata map[string]string `json:"metadata,omitempty"` + CertPEM string `json:"cert_pem"` + KeyPEM string `json:"key_pem,omitempty"` + ChainPEM string `json:"chain_pem"` + TargetConfig json.RawMessage `json:"target_config"` + Metadata map[string]string `json:"metadata,omitempty"` } // DeploymentResult contains the result of a successful certificate deployment. type DeploymentResult struct { - Success bool `json:"success"` - TargetAddress string `json:"target_address"` - DeploymentID string `json:"deployment_id"` - Message string `json:"message"` - DeployedAt time.Time `json:"deployed_at"` + Success bool `json:"success"` + TargetAddress string `json:"target_address"` + DeploymentID string `json:"deployment_id"` + Message string `json:"message"` + DeployedAt time.Time `json:"deployed_at"` Metadata map[string]string `json:"metadata,omitempty"` } // ValidationRequest contains the parameters for validating a deployed certificate. type ValidationRequest struct { - CertificateID string `json:"certificate_id"` - Serial string `json:"serial"` - TargetConfig json.RawMessage `json:"target_config"` + CertificateID string `json:"certificate_id"` + Serial string `json:"serial"` + TargetConfig json.RawMessage `json:"target_config"` Metadata map[string]string `json:"metadata,omitempty"` } // ValidationResult contains the result of a certificate validation check. type ValidationResult struct { - Valid bool `json:"valid"` - Serial string `json:"serial"` - TargetAddress string `json:"target_address"` - Message string `json:"message"` - ValidatedAt time.Time `json:"validated_at"` + Valid bool `json:"valid"` + Serial string `json:"serial"` + TargetAddress string `json:"target_address"` + Message string `json:"message"` + ValidatedAt time.Time `json:"validated_at"` Metadata map[string]string `json:"metadata,omitempty"` } diff --git a/internal/connector/target/nginx/nginx.go b/internal/connector/target/nginx/nginx.go index 3feab99..66154a5 100644 --- a/internal/connector/target/nginx/nginx.go +++ b/internal/connector/target/nginx/nginx.go @@ -15,10 +15,10 @@ import ( // Config represents the NGINX deployment target configuration. // This configuration is used on the agent side to deploy certificates to NGINX. type Config struct { - CertPath string `json:"cert_path"` // Path where cert will be written (typically /etc/nginx/certs/cert.pem) - KeyPath string `json:"key_path"` // Path where private key will be written (NOT provided by control plane) - ChainPath string `json:"chain_path"` // Path where chain will be written (typically /etc/nginx/certs/chain.pem) - ReloadCommand string `json:"reload_command"` // Command to reload NGINX (e.g., "nginx -s reload" or "systemctl reload nginx") + CertPath string `json:"cert_path"` // Path where cert will be written (typically /etc/nginx/certs/cert.pem) + KeyPath string `json:"key_path"` // Path where private key will be written (NOT provided by control plane) + ChainPath string `json:"chain_path"` // Path where chain will be written (typically /etc/nginx/certs/chain.pem) + ReloadCommand string `json:"reload_command"` // Command to reload NGINX (e.g., "nginx -s reload" or "systemctl reload nginx") ValidateCommand string `json:"validate_command"` // Command to validate NGINX config (e.g., "nginx -t") } @@ -157,9 +157,9 @@ func (c *Connector) DeployCertificate(ctx context.Context, request target.Deploy Message: "Certificate deployed and NGINX reloaded successfully", DeployedAt: time.Now(), Metadata: map[string]string{ - "cert_path": c.config.CertPath, - "chain_path": c.config.ChainPath, - "duration_ms": fmt.Sprintf("%d", deploymentDuration.Milliseconds()), + "cert_path": c.config.CertPath, + "chain_path": c.config.ChainPath, + "duration_ms": fmt.Sprintf("%d", deploymentDuration.Milliseconds()), }, }, nil } diff --git a/internal/domain/audit.go b/internal/domain/audit.go index 8846be2..55df86b 100644 --- a/internal/domain/audit.go +++ b/internal/domain/audit.go @@ -7,14 +7,14 @@ import ( // AuditEvent records an action taken in the control plane. type AuditEvent struct { - ID string `json:"id"` - Actor string `json:"actor"` - ActorType ActorType `json:"actor_type"` - Action string `json:"action"` - ResourceType string `json:"resource_type"` - ResourceID string `json:"resource_id"` + ID string `json:"id"` + Actor string `json:"actor"` + ActorType ActorType `json:"actor_type"` + Action string `json:"action"` + ResourceType string `json:"resource_type"` + ResourceID string `json:"resource_id"` Details json.RawMessage `json:"details"` - Timestamp time.Time `json:"timestamp"` + Timestamp time.Time `json:"timestamp"` } // ActorType represents the entity performing an action. diff --git a/internal/domain/certificate.go b/internal/domain/certificate.go index a53ef34..0622738 100644 --- a/internal/domain/certificate.go +++ b/internal/domain/certificate.go @@ -6,36 +6,36 @@ import ( // ManagedCertificate represents a certificate managed by the control plane. type ManagedCertificate struct { - ID string `json:"id"` - Name string `json:"name"` - CommonName string `json:"common_name"` - SANs []string `json:"sans"` - Environment string `json:"environment"` - OwnerID string `json:"owner_id"` - TeamID string `json:"team_id"` - IssuerID string `json:"issuer_id"` - TargetIDs []string `json:"target_ids"` - RenewalPolicyID string `json:"renewal_policy_id"` - Status CertificateStatus `json:"status"` - ExpiresAt time.Time `json:"expires_at"` - Tags map[string]string `json:"tags"` - LastRenewalAt *time.Time `json:"last_renewal_at,omitempty"` - LastDeploymentAt *time.Time `json:"last_deployment_at,omitempty"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID string `json:"id"` + Name string `json:"name"` + CommonName string `json:"common_name"` + SANs []string `json:"sans"` + Environment string `json:"environment"` + OwnerID string `json:"owner_id"` + TeamID string `json:"team_id"` + IssuerID string `json:"issuer_id"` + TargetIDs []string `json:"target_ids"` + RenewalPolicyID string `json:"renewal_policy_id"` + Status CertificateStatus `json:"status"` + ExpiresAt time.Time `json:"expires_at"` + Tags map[string]string `json:"tags"` + LastRenewalAt *time.Time `json:"last_renewal_at,omitempty"` + LastDeploymentAt *time.Time `json:"last_deployment_at,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } // CertificateVersion represents a specific version of a certificate. type CertificateVersion struct { - ID string `json:"id"` - CertificateID string `json:"certificate_id"` - SerialNumber string `json:"serial_number"` - NotBefore time.Time `json:"not_before"` - NotAfter time.Time `json:"not_after"` - FingerprintSHA256 string `json:"fingerprint_sha256"` - PEMChain string `json:"pem_chain"` - CSRPEM string `json:"csr_pem"` - CreatedAt time.Time `json:"created_at"` + ID string `json:"id"` + CertificateID string `json:"certificate_id"` + SerialNumber string `json:"serial_number"` + NotBefore time.Time `json:"not_before"` + NotAfter time.Time `json:"not_after"` + FingerprintSHA256 string `json:"fingerprint_sha256"` + PEMChain string `json:"pem_chain"` + CSRPEM string `json:"csr_pem"` + CreatedAt time.Time `json:"created_at"` } // CertificateStatus represents the lifecycle status of a managed certificate. diff --git a/internal/domain/connector.go b/internal/domain/connector.go index ebd0664..f40b84c 100644 --- a/internal/domain/connector.go +++ b/internal/domain/connector.go @@ -18,25 +18,25 @@ type Issuer struct { // DeploymentTarget represents a target system where certificates are deployed. type DeploymentTarget struct { - ID string `json:"id"` - Name string `json:"name"` - Type TargetType `json:"type"` - AgentID string `json:"agent_id"` - Config json.RawMessage `json:"config"` - Enabled bool `json:"enabled"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID string `json:"id"` + Name string `json:"name"` + Type TargetType `json:"type"` + AgentID string `json:"agent_id"` + Config json.RawMessage `json:"config"` + Enabled bool `json:"enabled"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } // Agent represents an agent running on a target system. type Agent struct { - ID string `json:"id"` - Name string `json:"name"` - Hostname string `json:"hostname"` - Status AgentStatus `json:"status"` - LastHeartbeatAt *time.Time `json:"last_heartbeat_at,omitempty"` - RegisteredAt time.Time `json:"registered_at"` - APIKeyHash string `json:"api_key_hash"` + ID string `json:"id"` + Name string `json:"name"` + Hostname string `json:"hostname"` + Status AgentStatus `json:"status"` + LastHeartbeatAt *time.Time `json:"last_heartbeat_at,omitempty"` + RegisteredAt time.Time `json:"registered_at"` + APIKeyHash string `json:"api_key_hash"` } // AgentStatus represents the operational status of an agent. @@ -52,7 +52,7 @@ const ( type IssuerType string const ( - IssuerTypeACME IssuerType = "ACME" + IssuerTypeACME IssuerType = "ACME" IssuerTypeGenericCA IssuerType = "GenericCA" ) diff --git a/internal/domain/job.go b/internal/domain/job.go index 91e5936..49c75ec 100644 --- a/internal/domain/job.go +++ b/internal/domain/job.go @@ -7,18 +7,18 @@ import ( // Job represents a unit of work in the certificate control plane. type Job struct { - ID string `json:"id"` - Type JobType `json:"type"` - CertificateID string `json:"certificate_id"` - TargetID *string `json:"target_id,omitempty"` - Status JobStatus `json:"status"` - Attempts int `json:"attempts"` - MaxAttempts int `json:"max_attempts"` - LastError *string `json:"last_error,omitempty"` - ScheduledAt time.Time `json:"scheduled_at"` - StartedAt *time.Time `json:"started_at,omitempty"` - CompletedAt *time.Time `json:"completed_at,omitempty"` - CreatedAt time.Time `json:"created_at"` + ID string `json:"id"` + Type JobType `json:"type"` + CertificateID string `json:"certificate_id"` + TargetID *string `json:"target_id,omitempty"` + Status JobStatus `json:"status"` + Attempts int `json:"attempts"` + MaxAttempts int `json:"max_attempts"` + LastError *string `json:"last_error,omitempty"` + ScheduledAt time.Time `json:"scheduled_at"` + StartedAt *time.Time `json:"started_at,omitempty"` + CompletedAt *time.Time `json:"completed_at,omitempty"` + CreatedAt time.Time `json:"created_at"` } // JobType represents the classification of work to be performed. @@ -45,9 +45,9 @@ const ( // DeploymentJob represents a job that deploys a certificate to a target via an agent. type DeploymentJob struct { - Job `json:"job"` - AgentID string `json:"agent_id"` - DeploymentResult json.RawMessage `json:"deployment_result,omitempty"` + Job `json:"job"` + AgentID string `json:"agent_id"` + DeploymentResult json.RawMessage `json:"deployment_result,omitempty"` } // WorkItem enriches a Job with target details so the agent knows which connector to use. diff --git a/internal/domain/policy.go b/internal/domain/policy.go index 7077d9a..aae73c0 100644 --- a/internal/domain/policy.go +++ b/internal/domain/policy.go @@ -7,13 +7,13 @@ import ( // PolicyRule defines enforcement rules for certificate management. type PolicyRule struct { - ID string `json:"id"` - Name string `json:"name"` - Type PolicyType `json:"type"` + ID string `json:"id"` + Name string `json:"name"` + Type PolicyType `json:"type"` Config json.RawMessage `json:"config"` - Enabled bool `json:"enabled"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + Enabled bool `json:"enabled"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } // PolicyType represents the category of policy enforcement. @@ -29,12 +29,12 @@ const ( // PolicyViolation records an instance of a certificate violating a policy rule. type PolicyViolation struct { - ID string `json:"id"` - CertificateID string `json:"certificate_id"` - RuleID string `json:"rule_id"` - Message string `json:"message"` - Severity PolicySeverity `json:"severity"` - CreatedAt time.Time `json:"created_at"` + ID string `json:"id"` + CertificateID string `json:"certificate_id"` + RuleID string `json:"rule_id"` + Message string `json:"message"` + Severity PolicySeverity `json:"severity"` + CreatedAt time.Time `json:"created_at"` } // PolicySeverity indicates the impact level of a policy violation. diff --git a/internal/integration/lifecycle_test.go b/internal/integration/lifecycle_test.go index 9839ad0..aeaba0f 100644 --- a/internal/integration/lifecycle_test.go +++ b/internal/integration/lifecycle_test.go @@ -910,8 +910,8 @@ func (m *mockIssuerRepository) Delete(ctx context.Context, id string) error { // Mock service implementations for handlers that need them but aren't tested type mockTargetService struct { - targetRepo *mockTargetRepository - auditService *service.AuditService + targetRepo *mockTargetRepository + auditService *service.AuditService } func (m *mockTargetService) ListTargets(page, perPage int) ([]domain.DeploymentTarget, int64, error) { diff --git a/internal/repository/filters.go b/internal/repository/filters.go index 6947afc..1eef7e0 100644 --- a/internal/repository/filters.go +++ b/internal/repository/filters.go @@ -24,9 +24,9 @@ type JobFilter struct { // AuditFilter defines filtering criteria for audit event queries. type AuditFilter struct { - Actor string // username or service ID - ActorType string // "user", "agent", "system" - ResourceType string // e.g., "certificate", "policy", "agent" + Actor string // username or service ID + ActorType string // "user", "agent", "system" + ResourceType string // e.g., "certificate", "policy", "agent" ResourceID string From time.Time To time.Time diff --git a/internal/scheduler/scheduler.go b/internal/scheduler/scheduler.go index 2193fb5..42185b3 100644 --- a/internal/scheduler/scheduler.go +++ b/internal/scheduler/scheduler.go @@ -19,9 +19,9 @@ type Scheduler struct { logger *slog.Logger // Configurable tick intervals - renewalCheckInterval time.Duration - jobProcessorInterval time.Duration - agentHealthCheckInterval time.Duration + renewalCheckInterval time.Duration + jobProcessorInterval time.Duration + agentHealthCheckInterval time.Duration notificationProcessInterval time.Duration } diff --git a/internal/service/agent.go b/internal/service/agent.go index 99677b3..ffa4a32 100644 --- a/internal/service/agent.go +++ b/internal/service/agent.go @@ -14,13 +14,13 @@ import ( // AgentService provides business logic for managing and coordinating with agents. type AgentService struct { - agentRepo repository.AgentRepository - certRepo repository.CertificateRepository - jobRepo repository.JobRepository - targetRepo repository.TargetRepository - auditService *AuditService - issuerRegistry map[string]IssuerConnector - renewalService *RenewalService + agentRepo repository.AgentRepository + certRepo repository.CertificateRepository + jobRepo repository.JobRepository + targetRepo repository.TargetRepository + auditService *AuditService + issuerRegistry map[string]IssuerConnector + renewalService *RenewalService } // NewAgentService creates a new agent service. @@ -161,7 +161,7 @@ func (s *AgentService) SubmitCSR(ctx context.Context, agentID string, certID str } version := &domain.CertificateVersion{ - ID: generateID("certver"), + ID: generateID("certver"), CertificateID: certID, SerialNumber: result.Serial, NotBefore: result.NotBefore, diff --git a/internal/service/certificate.go b/internal/service/certificate.go index 0769a3a..310de54 100644 --- a/internal/service/certificate.go +++ b/internal/service/certificate.go @@ -11,7 +11,7 @@ import ( // CertificateService provides business logic for certificate management. type CertificateService struct { - certRepo repository.CertificateRepository + certRepo repository.CertificateRepository policyService *PolicyService auditService *AuditService } @@ -23,7 +23,7 @@ func NewCertificateService( auditService *AuditService, ) *CertificateService { return &CertificateService{ - certRepo: certRepo, + certRepo: certRepo, policyService: policyService, auditService: auditService, } diff --git a/internal/service/certificate_test.go b/internal/service/certificate_test.go index 96743fb..bb111aa 100644 --- a/internal/service/certificate_test.go +++ b/internal/service/certificate_test.go @@ -18,8 +18,8 @@ func TestCreateCertificate(t *testing.T) { Events: []*domain.AuditEvent{}, } policyRepo := &mockPolicyRepo{ - Rules: make(map[string]*domain.PolicyRule), - Violations: []*domain.PolicyViolation{}, + Rules: make(map[string]*domain.PolicyRule), + Violations: []*domain.PolicyViolation{}, } policyService := NewPolicyService(policyRepo, NewAuditService(auditRepo)) diff --git a/internal/service/job.go b/internal/service/job.go index 37347c0..09b511a 100644 --- a/internal/service/job.go +++ b/internal/service/job.go @@ -12,10 +12,10 @@ import ( // JobService manages job processing and status tracking. // It coordinates between the scheduler and various job-specific services. type JobService struct { - jobRepo repository.JobRepository - renewalService *RenewalService - deploymentService *DeploymentService - logger *slog.Logger + jobRepo repository.JobRepository + renewalService *RenewalService + deploymentService *DeploymentService + logger *slog.Logger } // NewJobService creates a new job service. @@ -249,4 +249,3 @@ func (s *JobService) ListJobs(status, jobType string, page, perPage int) ([]doma func (s *JobService) GetJob(id string) (*domain.Job, error) { return s.jobRepo.Get(context.Background(), id) } - diff --git a/internal/service/notification.go b/internal/service/notification.go index e92e8b0..f0aa592 100644 --- a/internal/service/notification.go +++ b/internal/service/notification.go @@ -11,7 +11,7 @@ import ( // NotificationService provides business logic for managing notifications. type NotificationService struct { - notifRepo repository.NotificationRepository + notifRepo repository.NotificationRepository notifierRegistry map[string]Notifier } diff --git a/internal/service/policy_test.go b/internal/service/policy_test.go index 00d3471..9141c84 100644 --- a/internal/service/policy_test.go +++ b/internal/service/policy_test.go @@ -12,8 +12,8 @@ import ( func TestCreateRule(t *testing.T) { ctx := context.Background() policyRepo := &mockPolicyRepo{ - Rules: make(map[string]*domain.PolicyRule), - Violations: []*domain.PolicyViolation{}, + Rules: make(map[string]*domain.PolicyRule), + Violations: []*domain.PolicyViolation{}, } auditRepo := &mockAuditRepo{Events: []*domain.AuditEvent{}} auditService := NewAuditService(auditRepo) @@ -58,8 +58,8 @@ func TestGetRule(t *testing.T) { } policyRepo := &mockPolicyRepo{ - Rules: map[string]*domain.PolicyRule{"rule-001": rule}, - Violations: []*domain.PolicyViolation{}, + Rules: map[string]*domain.PolicyRule{"rule-001": rule}, + Violations: []*domain.PolicyViolation{}, } auditRepo := &mockAuditRepo{} auditService := NewAuditService(auditRepo) @@ -79,8 +79,8 @@ func TestGetRule(t *testing.T) { func TestGetRule_NotFound(t *testing.T) { ctx := context.Background() policyRepo := &mockPolicyRepo{ - Rules: make(map[string]*domain.PolicyRule), - Violations: []*domain.PolicyViolation{}, + Rules: make(map[string]*domain.PolicyRule), + Violations: []*domain.PolicyViolation{}, } auditRepo := &mockAuditRepo{} auditService := NewAuditService(auditRepo) @@ -115,8 +115,8 @@ func TestListRules(t *testing.T) { } policyRepo := &mockPolicyRepo{ - Rules: map[string]*domain.PolicyRule{"rule-001": rule1, "rule-002": rule2}, - Violations: []*domain.PolicyViolation{}, + Rules: map[string]*domain.PolicyRule{"rule-001": rule1, "rule-002": rule2}, + Violations: []*domain.PolicyViolation{}, } auditRepo := &mockAuditRepo{} auditService := NewAuditService(auditRepo) @@ -147,8 +147,8 @@ func TestUpdateRule(t *testing.T) { } policyRepo := &mockPolicyRepo{ - Rules: map[string]*domain.PolicyRule{"rule-001": originalRule}, - Violations: []*domain.PolicyViolation{}, + Rules: map[string]*domain.PolicyRule{"rule-001": originalRule}, + Violations: []*domain.PolicyViolation{}, } auditRepo := &mockAuditRepo{Events: []*domain.AuditEvent{}} auditService := NewAuditService(auditRepo) @@ -187,8 +187,8 @@ func TestDeleteRule(t *testing.T) { } policyRepo := &mockPolicyRepo{ - Rules: map[string]*domain.PolicyRule{"rule-001": rule}, - Violations: []*domain.PolicyViolation{}, + Rules: map[string]*domain.PolicyRule{"rule-001": rule}, + Violations: []*domain.PolicyViolation{}, } auditRepo := &mockAuditRepo{Events: []*domain.AuditEvent{}} auditService := NewAuditService(auditRepo) @@ -223,8 +223,8 @@ func TestValidateCertificate(t *testing.T) { } policyRepo := &mockPolicyRepo{ - Rules: map[string]*domain.PolicyRule{"rule-001": rule}, - Violations: []*domain.PolicyViolation{}, + Rules: map[string]*domain.PolicyRule{"rule-001": rule}, + Violations: []*domain.PolicyViolation{}, } auditRepo := &mockAuditRepo{} auditService := NewAuditService(auditRepo) @@ -265,8 +265,8 @@ func TestValidateCertificate_WithViolation(t *testing.T) { } policyRepo := &mockPolicyRepo{ - Rules: map[string]*domain.PolicyRule{"rule-001": rule}, - Violations: []*domain.PolicyViolation{}, + Rules: map[string]*domain.PolicyRule{"rule-001": rule}, + Violations: []*domain.PolicyViolation{}, } auditRepo := &mockAuditRepo{} auditService := NewAuditService(auditRepo) @@ -319,8 +319,8 @@ func TestValidateCertificate_MultipleViolations(t *testing.T) { } policyRepo := &mockPolicyRepo{ - Rules: map[string]*domain.PolicyRule{"rule-001": rule1, "rule-002": rule2}, - Violations: []*domain.PolicyViolation{}, + Rules: map[string]*domain.PolicyRule{"rule-001": rule1, "rule-002": rule2}, + Violations: []*domain.PolicyViolation{}, } auditRepo := &mockAuditRepo{} auditService := NewAuditService(auditRepo) @@ -330,7 +330,7 @@ func TestValidateCertificate_MultipleViolations(t *testing.T) { cert := &domain.ManagedCertificate{ ID: "cert-001", CommonName: "example.com", - IssuerID: "", // Missing issuer + IssuerID: "", // Missing issuer Tags: nil, // Missing metadata Status: domain.CertificateStatusActive, ExpiresAt: now.AddDate(1, 0, 0), @@ -368,8 +368,8 @@ func TestListPolicies(t *testing.T) { } policyRepo := &mockPolicyRepo{ - Rules: map[string]*domain.PolicyRule{"rule-001": rule1, "rule-002": rule2}, - Violations: []*domain.PolicyViolation{}, + Rules: map[string]*domain.PolicyRule{"rule-001": rule1, "rule-002": rule2}, + Violations: []*domain.PolicyViolation{}, } auditRepo := &mockAuditRepo{} auditService := NewAuditService(auditRepo) @@ -392,8 +392,8 @@ func TestListPolicies(t *testing.T) { func TestCreatePolicy(t *testing.T) { now := time.Now() policyRepo := &mockPolicyRepo{ - Rules: make(map[string]*domain.PolicyRule), - Violations: []*domain.PolicyViolation{}, + Rules: make(map[string]*domain.PolicyRule), + Violations: []*domain.PolicyViolation{}, } auditRepo := &mockAuditRepo{} auditService := NewAuditService(auditRepo) diff --git a/internal/service/renewal.go b/internal/service/renewal.go index 87f2531..10c427a 100644 --- a/internal/service/renewal.go +++ b/internal/service/renewal.go @@ -204,7 +204,7 @@ func (s *RenewalService) sendThresholdAlerts(ctx context.Context, cert *domain.M _ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem, "expiration_alert_sent", "certificate", cert.ID, map[string]interface{}{ - "threshold_days": threshold, + "threshold_days": threshold, "days_until_expiry": daysUntil, }) } @@ -356,14 +356,14 @@ func (s *RenewalService) processRenewalServerKeygen(ctx context.Context, job *do // Create new certificate version version := &domain.CertificateVersion{ ID: generateID("certver"), - CertificateID: job.CertificateID, - SerialNumber: result.Serial, - NotBefore: result.NotBefore, - NotAfter: result.NotAfter, + CertificateID: job.CertificateID, + SerialNumber: result.Serial, + NotBefore: result.NotBefore, + NotAfter: result.NotAfter, FingerprintSHA256: fingerprint, - PEMChain: result.CertPEM + "\n" + result.ChainPEM, - CSRPEM: privKeyPEM, // Server mode: stores private key for agent deployment - CreatedAt: time.Now(), + PEMChain: result.CertPEM + "\n" + result.ChainPEM, + CSRPEM: privKeyPEM, // Server mode: stores private key for agent deployment + CreatedAt: time.Now(), } if err := s.certRepo.CreateVersion(ctx, version); err != nil { @@ -439,14 +439,14 @@ func (s *RenewalService) CompleteAgentCSRRenewal(ctx context.Context, job *domai // Store cert version — CSRPEM holds the actual CSR (not the private key!) version := &domain.CertificateVersion{ ID: generateID("certver"), - CertificateID: cert.ID, - SerialNumber: result.Serial, - NotBefore: result.NotBefore, - NotAfter: result.NotAfter, + CertificateID: cert.ID, + SerialNumber: result.Serial, + NotBefore: result.NotBefore, + NotAfter: result.NotAfter, FingerprintSHA256: fingerprint, - PEMChain: result.CertPEM + "\n" + result.ChainPEM, - CSRPEM: csrPEM, // Agent mode: stores actual CSR, not private key - CreatedAt: time.Now(), + PEMChain: result.CertPEM + "\n" + result.ChainPEM, + CSRPEM: csrPEM, // Agent mode: stores actual CSR, not private key + CreatedAt: time.Now(), } if err := s.certRepo.CreateVersion(ctx, version); err != nil { diff --git a/internal/service/testutil_test.go b/internal/service/testutil_test.go index 5c55cb0..2f81a5d 100644 --- a/internal/service/testutil_test.go +++ b/internal/service/testutil_test.go @@ -13,16 +13,16 @@ var errNotFound = errors.New("not found") // mockCertRepo is a test implementation of CertificateRepository type mockCertRepo struct { - Certs map[string]*domain.ManagedCertificate - Versions map[string][]*domain.CertificateVersion - CreateErr error - UpdateErr error - GetErr error - ListErr error - ListVersionsErr error + Certs map[string]*domain.ManagedCertificate + Versions map[string][]*domain.CertificateVersion + CreateErr error + UpdateErr error + GetErr error + ListErr error + ListVersionsErr error ListVersionsResult []*domain.CertificateVersion - CreateVersionErr error - ArchiveErr error + CreateVersionErr error + ArchiveErr error } func (m *mockCertRepo) List(ctx context.Context, filter *repository.CertificateFilter) ([]*domain.ManagedCertificate, int, error) { @@ -109,15 +109,15 @@ func (m *mockCertRepo) AddCert(cert *domain.ManagedCertificate) { // mockJobRepo is a test implementation of JobRepository type mockJobRepo struct { - Jobs map[string]*domain.Job - StatusUpdates map[string]domain.JobStatus - CreateErr error - UpdateErr error - UpdateStatusErr error - GetErr error - ListErr error - ListByStatusErr error - DeleteErr error + Jobs map[string]*domain.Job + StatusUpdates map[string]domain.JobStatus + CreateErr error + UpdateErr error + UpdateStatusErr error + GetErr error + ListErr error + ListByStatusErr error + DeleteErr error } func (m *mockJobRepo) List(ctx context.Context) ([]*domain.Job, error) { @@ -261,7 +261,7 @@ func (m *mockNotifRepo) AddNotification(notif *domain.NotificationEvent) { // mockAuditRepo is a test implementation of AuditRepository type mockAuditRepo struct { - Events []*domain.AuditEvent + Events []*domain.AuditEvent CreateErr error ListErr error } @@ -309,15 +309,15 @@ func (m *mockAuditRepo) AddEvent(event *domain.AuditEvent) { // mockPolicyRepo is a test implementation of PolicyRepository type mockPolicyRepo struct { - Rules map[string]*domain.PolicyRule - Violations []*domain.PolicyViolation - CreateRuleErr error - UpdateRuleErr error - DeleteRuleErr error - GetRuleErr error - ListRulesErr error - CreateViolationErr error - ListViolationsErr error + Rules map[string]*domain.PolicyRule + Violations []*domain.PolicyViolation + CreateRuleErr error + UpdateRuleErr error + DeleteRuleErr error + GetRuleErr error + ListRulesErr error + CreateViolationErr error + ListViolationsErr error } func (m *mockPolicyRepo) ListRules(ctx context.Context) ([]*domain.PolicyRule, error) { @@ -420,13 +420,13 @@ func (m *mockRenewalPolicyRepo) AddPolicy(policy *domain.RenewalPolicy) { // mockAgentRepo is a test implementation of AgentRepository type mockAgentRepo struct { - Agents map[string]*domain.Agent - HeartbeatUpdates map[string]time.Time - CreateErr error - UpdateErr error - DeleteErr error - GetErr error - ListErr error + Agents map[string]*domain.Agent + HeartbeatUpdates map[string]time.Time + CreateErr error + UpdateErr error + DeleteErr error + GetErr error + ListErr error UpdateHeartbeatErr error GetByAPIKeyErr error } @@ -509,13 +509,13 @@ func (m *mockAgentRepo) AddAgent(agent *domain.Agent) { // mockTargetRepo is a test implementation of TargetRepository type mockTargetRepo struct { - Targets map[string]*domain.DeploymentTarget - CreateErr error - UpdateErr error - DeleteErr error - GetErr error - ListErr error - ListByCertErr error + Targets map[string]*domain.DeploymentTarget + CreateErr error + UpdateErr error + DeleteErr error + GetErr error + ListErr error + ListByCertErr error } func (m *mockTargetRepo) List(ctx context.Context) ([]*domain.DeploymentTarget, error) { @@ -667,9 +667,9 @@ func newMockIssuerRepository() *mockIssuerRepository { // mockIssuerRepository is a test implementation of IssuerRepository type mockIssuerRepository struct { - issuers map[string]*domain.Issuer - GetErr error - ListErr error + issuers map[string]*domain.Issuer + GetErr error + ListErr error CreateErr error UpdateErr error DeleteErr error