mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 12:41:30 +00:00
5d98e373e3
Implements core revocation infrastructure: POST /api/v1/certificates/{id}/revoke
with all 8 RFC 5280 reason codes, JSON-formatted CRL at GET /api/v1/crl, webhook
and email revocation notifications, best-effort issuer notification, and immutable
revocation audit trail. Includes 48 new tests across service, handler, integration,
and domain layers (600+ total). Fixes 3 pre-existing test bugs (team_test error
matching, agent_group delete status code, team handler per_page validation).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
179 lines
8.4 KiB
Go
179 lines
8.4 KiB
Go
package router
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/shankar0123/certctl/internal/api/handler"
|
|
"github.com/shankar0123/certctl/internal/api/middleware"
|
|
)
|
|
|
|
// Router wraps http.ServeMux and manages route registration with middleware.
|
|
type Router struct {
|
|
mux *http.ServeMux
|
|
middleware []func(http.Handler) http.Handler
|
|
}
|
|
|
|
// New creates a new Router instance.
|
|
func New() *Router {
|
|
return &Router{
|
|
mux: http.NewServeMux(),
|
|
middleware: []func(http.Handler) http.Handler{},
|
|
}
|
|
}
|
|
|
|
// NewWithMiddleware creates a Router with initial middleware stack.
|
|
func NewWithMiddleware(middlewares ...func(http.Handler) http.Handler) *Router {
|
|
r := New()
|
|
r.middleware = middlewares
|
|
return r
|
|
}
|
|
|
|
// ServeHTTP implements http.Handler interface.
|
|
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
r.mux.ServeHTTP(w, req)
|
|
}
|
|
|
|
// Register registers a handler for a given path with the middleware chain applied.
|
|
func (r *Router) Register(pattern string, handler http.Handler) {
|
|
r.mux.Handle(pattern, middleware.Chain(handler, r.middleware...))
|
|
}
|
|
|
|
// RegisterFunc registers a handler function for a given path.
|
|
func (r *Router) RegisterFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
|
r.Register(pattern, http.HandlerFunc(handler))
|
|
}
|
|
|
|
// RegisterHandlers sets up all API routes with their handlers.
|
|
func (r *Router) RegisterHandlers(
|
|
certificates handler.CertificateHandler,
|
|
issuers handler.IssuerHandler,
|
|
targets handler.TargetHandler,
|
|
agents handler.AgentHandler,
|
|
jobs handler.JobHandler,
|
|
policies handler.PolicyHandler,
|
|
profiles handler.ProfileHandler,
|
|
teams handler.TeamHandler,
|
|
owners handler.OwnerHandler,
|
|
agentGroups handler.AgentGroupHandler,
|
|
audit handler.AuditHandler,
|
|
notifications handler.NotificationHandler,
|
|
health handler.HealthHandler,
|
|
) {
|
|
// Health endpoints (no auth middleware — must always be accessible)
|
|
r.mux.Handle("GET /health", middleware.Chain(
|
|
http.HandlerFunc(health.Health),
|
|
middleware.CORS,
|
|
middleware.ContentType,
|
|
))
|
|
r.mux.Handle("GET /ready", middleware.Chain(
|
|
http.HandlerFunc(health.Ready),
|
|
middleware.CORS,
|
|
middleware.ContentType,
|
|
))
|
|
// Auth info endpoint (no auth middleware — GUI needs this before login)
|
|
r.mux.Handle("GET /api/v1/auth/info", middleware.Chain(
|
|
http.HandlerFunc(health.AuthInfo),
|
|
middleware.CORS,
|
|
middleware.ContentType,
|
|
))
|
|
// Auth check endpoint (uses full middleware chain via r.Register)
|
|
r.Register("GET /api/v1/auth/check", http.HandlerFunc(health.AuthCheck))
|
|
|
|
// Certificates routes: /api/v1/certificates
|
|
r.Register("GET /api/v1/certificates", http.HandlerFunc(certificates.ListCertificates))
|
|
r.Register("POST /api/v1/certificates", http.HandlerFunc(certificates.CreateCertificate))
|
|
r.Register("GET /api/v1/certificates/{id}", http.HandlerFunc(certificates.GetCertificate))
|
|
r.Register("PUT /api/v1/certificates/{id}", http.HandlerFunc(certificates.UpdateCertificate))
|
|
r.Register("DELETE /api/v1/certificates/{id}", http.HandlerFunc(certificates.ArchiveCertificate))
|
|
r.Register("GET /api/v1/certificates/{id}/versions", http.HandlerFunc(certificates.GetCertificateVersions))
|
|
r.Register("POST /api/v1/certificates/{id}/renew", http.HandlerFunc(certificates.TriggerRenewal))
|
|
r.Register("POST /api/v1/certificates/{id}/deploy", http.HandlerFunc(certificates.TriggerDeployment))
|
|
r.Register("POST /api/v1/certificates/{id}/revoke", http.HandlerFunc(certificates.RevokeCertificate))
|
|
|
|
// CRL endpoint: /api/v1/crl
|
|
r.Register("GET /api/v1/crl", http.HandlerFunc(certificates.GetCRL))
|
|
|
|
// Issuers routes: /api/v1/issuers
|
|
r.Register("GET /api/v1/issuers", http.HandlerFunc(issuers.ListIssuers))
|
|
r.Register("POST /api/v1/issuers", http.HandlerFunc(issuers.CreateIssuer))
|
|
r.Register("GET /api/v1/issuers/{id}", http.HandlerFunc(issuers.GetIssuer))
|
|
r.Register("PUT /api/v1/issuers/{id}", http.HandlerFunc(issuers.UpdateIssuer))
|
|
r.Register("DELETE /api/v1/issuers/{id}", http.HandlerFunc(issuers.DeleteIssuer))
|
|
r.Register("POST /api/v1/issuers/{id}/test", http.HandlerFunc(issuers.TestConnection))
|
|
|
|
// Targets routes: /api/v1/targets
|
|
r.Register("GET /api/v1/targets", http.HandlerFunc(targets.ListTargets))
|
|
r.Register("POST /api/v1/targets", http.HandlerFunc(targets.CreateTarget))
|
|
r.Register("GET /api/v1/targets/{id}", http.HandlerFunc(targets.GetTarget))
|
|
r.Register("PUT /api/v1/targets/{id}", http.HandlerFunc(targets.UpdateTarget))
|
|
r.Register("DELETE /api/v1/targets/{id}", http.HandlerFunc(targets.DeleteTarget))
|
|
|
|
// Agents routes: /api/v1/agents
|
|
r.Register("GET /api/v1/agents", http.HandlerFunc(agents.ListAgents))
|
|
r.Register("POST /api/v1/agents", http.HandlerFunc(agents.RegisterAgent))
|
|
r.Register("GET /api/v1/agents/{id}", http.HandlerFunc(agents.GetAgent))
|
|
r.Register("POST /api/v1/agents/{id}/heartbeat", http.HandlerFunc(agents.Heartbeat))
|
|
r.Register("POST /api/v1/agents/{id}/csr", http.HandlerFunc(agents.AgentCSRSubmit))
|
|
r.Register("GET /api/v1/agents/{id}/certificates/{cert_id}", http.HandlerFunc(agents.AgentCertificatePickup))
|
|
r.Register("GET /api/v1/agents/{id}/work", http.HandlerFunc(agents.AgentGetWork))
|
|
r.Register("POST /api/v1/agents/{id}/jobs/{job_id}/status", http.HandlerFunc(agents.AgentReportJobStatus))
|
|
|
|
// Jobs routes: /api/v1/jobs
|
|
r.Register("GET /api/v1/jobs", http.HandlerFunc(jobs.ListJobs))
|
|
r.Register("GET /api/v1/jobs/{id}", http.HandlerFunc(jobs.GetJob))
|
|
r.Register("POST /api/v1/jobs/{id}/cancel", http.HandlerFunc(jobs.CancelJob))
|
|
r.Register("POST /api/v1/jobs/{id}/approve", http.HandlerFunc(jobs.ApproveJob))
|
|
r.Register("POST /api/v1/jobs/{id}/reject", http.HandlerFunc(jobs.RejectJob))
|
|
|
|
// Policies routes: /api/v1/policies
|
|
r.Register("GET /api/v1/policies", http.HandlerFunc(policies.ListPolicies))
|
|
r.Register("POST /api/v1/policies", http.HandlerFunc(policies.CreatePolicy))
|
|
r.Register("GET /api/v1/policies/{id}", http.HandlerFunc(policies.GetPolicy))
|
|
r.Register("PUT /api/v1/policies/{id}", http.HandlerFunc(policies.UpdatePolicy))
|
|
r.Register("DELETE /api/v1/policies/{id}", http.HandlerFunc(policies.DeletePolicy))
|
|
r.Register("GET /api/v1/policies/{id}/violations", http.HandlerFunc(policies.ListViolations))
|
|
|
|
// Profiles routes: /api/v1/profiles
|
|
r.Register("GET /api/v1/profiles", http.HandlerFunc(profiles.ListProfiles))
|
|
r.Register("POST /api/v1/profiles", http.HandlerFunc(profiles.CreateProfile))
|
|
r.Register("GET /api/v1/profiles/{id}", http.HandlerFunc(profiles.GetProfile))
|
|
r.Register("PUT /api/v1/profiles/{id}", http.HandlerFunc(profiles.UpdateProfile))
|
|
r.Register("DELETE /api/v1/profiles/{id}", http.HandlerFunc(profiles.DeleteProfile))
|
|
|
|
// Teams routes: /api/v1/teams
|
|
r.Register("GET /api/v1/teams", http.HandlerFunc(teams.ListTeams))
|
|
r.Register("POST /api/v1/teams", http.HandlerFunc(teams.CreateTeam))
|
|
r.Register("GET /api/v1/teams/{id}", http.HandlerFunc(teams.GetTeam))
|
|
r.Register("PUT /api/v1/teams/{id}", http.HandlerFunc(teams.UpdateTeam))
|
|
r.Register("DELETE /api/v1/teams/{id}", http.HandlerFunc(teams.DeleteTeam))
|
|
|
|
// Owners routes: /api/v1/owners
|
|
r.Register("GET /api/v1/owners", http.HandlerFunc(owners.ListOwners))
|
|
r.Register("POST /api/v1/owners", http.HandlerFunc(owners.CreateOwner))
|
|
r.Register("GET /api/v1/owners/{id}", http.HandlerFunc(owners.GetOwner))
|
|
r.Register("PUT /api/v1/owners/{id}", http.HandlerFunc(owners.UpdateOwner))
|
|
r.Register("DELETE /api/v1/owners/{id}", http.HandlerFunc(owners.DeleteOwner))
|
|
|
|
// Agent Groups routes: /api/v1/agent-groups
|
|
r.Register("GET /api/v1/agent-groups", http.HandlerFunc(agentGroups.ListAgentGroups))
|
|
r.Register("POST /api/v1/agent-groups", http.HandlerFunc(agentGroups.CreateAgentGroup))
|
|
r.Register("GET /api/v1/agent-groups/{id}", http.HandlerFunc(agentGroups.GetAgentGroup))
|
|
r.Register("PUT /api/v1/agent-groups/{id}", http.HandlerFunc(agentGroups.UpdateAgentGroup))
|
|
r.Register("DELETE /api/v1/agent-groups/{id}", http.HandlerFunc(agentGroups.DeleteAgentGroup))
|
|
r.Register("GET /api/v1/agent-groups/{id}/members", http.HandlerFunc(agentGroups.ListAgentGroupMembers))
|
|
|
|
// Audit routes: /api/v1/audit
|
|
r.Register("GET /api/v1/audit", http.HandlerFunc(audit.ListAuditEvents))
|
|
r.Register("GET /api/v1/audit/{id}", http.HandlerFunc(audit.GetAuditEvent))
|
|
|
|
// Notifications routes: /api/v1/notifications
|
|
r.Register("GET /api/v1/notifications", http.HandlerFunc(notifications.ListNotifications))
|
|
r.Register("GET /api/v1/notifications/{id}", http.HandlerFunc(notifications.GetNotification))
|
|
r.Register("POST /api/v1/notifications/{id}/read", http.HandlerFunc(notifications.MarkAsRead))
|
|
}
|
|
|
|
// GetMux returns the underlying http.ServeMux for direct access if needed.
|
|
func (r *Router) GetMux() *http.ServeMux {
|
|
return r.mux
|
|
}
|