pki-manager/internal/api/handlers.go

381 lines
9.9 KiB
Go

package api
import (
"net/http"
"pki-manager/internal/models"
"pki-manager/internal/repository"
"pki-manager/internal/services"
"github.com/gin-gonic/gin"
)
type Handlers struct {
repo repository.Repository
cryptoService *services.CryptoService
}
func NewHandlers(repo repository.Repository, cryptoService *services.CryptoService) *Handlers {
return &Handlers{
repo: repo,
cryptoService: cryptoService,
}
}
// CA Handlers
func (h *Handlers) CreateCA(c *gin.Context) {
var req models.CreateCARequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
ca, err := h.cryptoService.GenerateRootCA(req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if err := h.repo.CreateCA(c.Request.Context(), ca); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, ca)
}
func (h *Handlers) GetCA(c *gin.Context) {
id := c.Param("id")
ca, err := h.repo.GetCA(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "CA not found"})
return
}
// Don't expose private key in GET requests
ca.PrivateKey = ""
c.JSON(http.StatusOK, ca)
}
// GetAllCAs - Retourne toujours un tableau, même vide
func (h *Handlers) GetAllCAs(c *gin.Context) {
cas, err := h.repo.GetAllCAs(c.Request.Context())
if err != nil {
// IMPORTANT: Retourner un tableau vide, pas une erreur
c.JSON(http.StatusOK, []interface{}{})
return
}
// Remove private keys from response
for _, ca := range cas {
ca.PrivateKey = ""
}
// S'assurer qu'on retourne toujours un tableau
if cas == nil {
c.JSON(http.StatusOK, []interface{}{})
} else {
c.JSON(http.StatusOK, cas)
}
}
func (h *Handlers) UpdateCA(c *gin.Context) {
id := c.Param("id")
var req models.UpdateCARequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
updates := make(map[string]interface{})
if req.Name != "" {
updates["name"] = req.Name
}
if req.Organization != "" {
updates["organization"] = req.Organization
}
if req.Email != "" {
updates["email"] = req.Email
}
if err := h.repo.UpdateCA(c.Request.Context(), id, updates); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "CA updated successfully"})
}
func (h *Handlers) DeleteCA(c *gin.Context) {
id := c.Param("id")
if err := h.repo.DeleteCA(c.Request.Context(), id); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "CA deleted successfully"})
}
// SubCA Handlers
func (h *Handlers) CreateSubCA(c *gin.Context) {
var req models.CreateSubCARequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Get parent CA
parentCA, err := h.repo.GetCA(c.Request.Context(), req.ParentCAID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Parent CA not found"})
return
}
subca, err := h.cryptoService.GenerateSubCA(req, parentCA)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if err := h.repo.CreateSubCA(c.Request.Context(), subca); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, subca)
}
func (h *Handlers) GetSubCA(c *gin.Context) {
id := c.Param("id")
subca, err := h.repo.GetSubCA(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "SubCA not found"})
return
}
subca.PrivateKey = ""
c.JSON(http.StatusOK, subca)
}
// GetAllSubCAs - Retourne toujours un tableau, même vide
func (h *Handlers) GetAllSubCAs(c *gin.Context) {
subcas, err := h.repo.GetAllSubCAs(c.Request.Context())
if err != nil {
// IMPORTANT: Retourner un tableau vide, pas une erreur
c.JSON(http.StatusOK, []interface{}{})
return
}
// Remove private keys from response
for _, subca := range subcas {
subca.PrivateKey = ""
}
// S'assurer qu'on retourne toujours un tableau
if subcas == nil {
c.JSON(http.StatusOK, []interface{}{})
} else {
c.JSON(http.StatusOK, subcas)
}
}
func (h *Handlers) UpdateSubCA(c *gin.Context) {
id := c.Param("id")
var req models.UpdateSubCARequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
updates := make(map[string]interface{})
if req.Name != "" {
updates["name"] = req.Name
}
if req.Organization != "" {
updates["organization"] = req.Organization
}
if req.Email != "" {
updates["email"] = req.Email
}
if err := h.repo.UpdateSubCA(c.Request.Context(), id, updates); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "SubCA updated successfully"})
}
func (h *Handlers) DeleteSubCA(c *gin.Context) {
id := c.Param("id")
if err := h.repo.DeleteSubCA(c.Request.Context(), id); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "SubCA deleted successfully"})
}
// Certificate Handlers
func (h *Handlers) CreateCertificate(c *gin.Context) {
var req models.CreateCertificateRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Try to get issuer as CA first
issuer, err := h.repo.GetCA(c.Request.Context(), req.IssuerCAID)
if err != nil {
// If not found as CA, try as SubCA
issuer, err := h.repo.GetSubCA(c.Request.Context(), req.IssuerCAID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Issuer not found"})
return
}
cert, err := h.cryptoService.GenerateCertificate(req, issuer)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if err := h.repo.CreateCertificate(c.Request.Context(), cert); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, cert)
return
}
cert, err := h.cryptoService.GenerateCertificate(req, issuer)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if err := h.repo.CreateCertificate(c.Request.Context(), cert); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, cert)
}
func (h *Handlers) GetCertificate(c *gin.Context) {
id := c.Param("id")
cert, err := h.repo.GetCertificate(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Certificate not found"})
return
}
cert.PrivateKey = ""
c.JSON(http.StatusOK, cert)
}
// GetAllCertificates - Retourne toujours un tableau, même vide
func (h *Handlers) GetAllCertificates(c *gin.Context) {
certs, err := h.repo.GetAllCertificates(c.Request.Context())
if err != nil {
// IMPORTANT: Retourner un tableau vide, pas une erreur
c.JSON(http.StatusOK, []interface{}{})
return
}
// Remove private keys from response
for _, cert := range certs {
cert.PrivateKey = ""
}
// S'assurer qu'on retourne toujours un tableau
if certs == nil {
c.JSON(http.StatusOK, []interface{}{})
} else {
c.JSON(http.StatusOK, certs)
}
}
func (h *Handlers) DeleteCertificate(c *gin.Context) {
id := c.Param("id")
if err := h.repo.DeleteCertificate(c.Request.Context(), id); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Certificate deleted successfully"})
}
func (h *Handlers) RevokeCertificate(c *gin.Context) {
id := c.Param("id")
var req models.RevokeCertificateRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.repo.RevokeCertificate(c.Request.Context(), id, req.Reason); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Certificate revoked successfully"})
}
// Download handlers for CA and SubCA
func (h *Handlers) DownloadCACertificate(c *gin.Context) {
id := c.Param("id")
ca, err := h.repo.GetCA(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "CA not found"})
return
}
c.Header("Content-Type", "application/x-pem-file")
c.Header("Content-Disposition", "attachment; filename="+ca.CommonName+".crt")
c.String(http.StatusOK, ca.Certificate)
}
func (h *Handlers) DownloadSubCACertificate(c *gin.Context) {
id := c.Param("id")
subca, err := h.repo.GetSubCA(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "SubCA not found"})
return
}
c.Header("Content-Type", "application/x-pem-file")
c.Header("Content-Disposition", "attachment; filename="+subca.CommonName+".crt")
c.String(http.StatusOK, subca.Certificate)
}
// Download handlers
func (h *Handlers) DownloadCertificate(c *gin.Context) {
id := c.Param("id")
cert, err := h.repo.GetCertificate(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Certificate not found"})
return
}
c.Header("Content-Type", "application/x-pem-file")
c.Header("Content-Disposition", "attachment; filename="+cert.CommonName+".crt")
c.String(http.StatusOK, cert.Certificate)
}
func (h *Handlers) DownloadPrivateKey(c *gin.Context) {
id := c.Param("id")
cert, err := h.repo.GetCertificate(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Certificate not found"})
return
}
c.Header("Content-Type", "application/x-pem-file")
c.Header("Content-Disposition", "attachment; filename="+cert.CommonName+".key")
c.String(http.StatusOK, cert.PrivateKey)
}
// Web Interface
func (h *Handlers) ServeWebInterface(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
}