381 lines
9.9 KiB
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)
|
|
}
|