First commit
This commit is contained in:
380
internal/api/handlers.go
Normal file
380
internal/api/handlers.go
Normal file
@@ -0,0 +1,380 @@
|
||||
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)
|
||||
}
|
||||
67
internal/api/routes.go
Normal file
67
internal/api/routes.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"log"
|
||||
"pki-manager/config"
|
||||
"pki-manager/internal/repository"
|
||||
"pki-manager/internal/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func SetupRoutes(router *gin.Engine, repo repository.Repository, jwtSecret string) {
|
||||
cfg := config.LoadConfig()
|
||||
cryptoService := services.NewCryptoService(cfg.CertsPath)
|
||||
handlers := NewHandlers(repo, cryptoService)
|
||||
|
||||
// Add logging middleware
|
||||
router.Use(func(c *gin.Context) {
|
||||
log.Printf("[API] %s %s", c.Request.Method, c.Request.URL.Path)
|
||||
c.Next()
|
||||
})
|
||||
|
||||
// Load HTML templates
|
||||
router.LoadHTMLGlob("internal/web/templates/*")
|
||||
router.Static("/static", "internal/web/static")
|
||||
|
||||
// Web interface
|
||||
router.GET("/", handlers.ServeWebInterface)
|
||||
|
||||
// API routes
|
||||
api := router.Group("/api/v1")
|
||||
{
|
||||
// CA routes
|
||||
ca := api.Group("/cas")
|
||||
{
|
||||
ca.POST("/", handlers.CreateCA)
|
||||
ca.GET("/", handlers.GetAllCAs)
|
||||
ca.GET("/:id", handlers.GetCA)
|
||||
ca.PUT("/:id", handlers.UpdateCA)
|
||||
ca.DELETE("/:id", handlers.DeleteCA)
|
||||
ca.GET("/:id/download/cert", handlers.DownloadCACertificate)
|
||||
}
|
||||
|
||||
// SubCA routes
|
||||
subca := api.Group("/subcas")
|
||||
{
|
||||
subca.POST("/", handlers.CreateSubCA)
|
||||
subca.GET("/", handlers.GetAllSubCAs)
|
||||
subca.GET("/:id", handlers.GetSubCA)
|
||||
subca.PUT("/:id", handlers.UpdateSubCA)
|
||||
subca.DELETE("/:id", handlers.DeleteSubCA)
|
||||
subca.GET("/:id/download/cert", handlers.DownloadSubCACertificate)
|
||||
}
|
||||
|
||||
// Certificate routes
|
||||
cert := api.Group("/certificates")
|
||||
{
|
||||
cert.POST("/", handlers.CreateCertificate)
|
||||
cert.GET("/", handlers.GetAllCertificates)
|
||||
cert.GET("/:id", handlers.GetCertificate)
|
||||
cert.DELETE("/:id", handlers.DeleteCertificate)
|
||||
cert.POST("/:id/revoke", handlers.RevokeCertificate)
|
||||
cert.GET("/:id/download/cert", handlers.DownloadCertificate)
|
||||
cert.GET("/:id/download/key", handlers.DownloadPrivateKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user