package handler import ( "github.com/gofiber/fiber/v2" "github.com/gosec/gsc-ops-api/internal/middleware" "github.com/gosec/gsc-ops-api/internal/schema" "github.com/gosec/gsc-ops-api/internal/service" "github.com/gosec/gsc-ops-api/pkg/types" ) // LDAPEntityHandler handles generic LDAP entity endpoints type LDAPEntityHandler struct { svc *service.LDAPEntityService registry *schema.Registry } // NewLDAPEntityHandler creates a new entity handler func NewLDAPEntityHandler(svc *service.LDAPEntityService, registry *schema.Registry) *LDAPEntityHandler { return &LDAPEntityHandler{svc: svc, registry: registry} } // ListTypes handles GET /api/v1/ldap/entities — list available entity types func (h *LDAPEntityHandler) ListTypes(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) allTypes := h.registry.AllEntityTypes() result := make([]fiber.Map, 0, len(allTypes)) for name, et := range allTypes { result = append(result, fiber.Map{ "name": name, "description": et.Description, "rdnAttribute": et.RDNAttribute, "domain": et.Domain, "requiredAttrs": et.RequiredAttrs, }) } return c.JSON(types.NewDataResponse(result, reqID)) } // List handles GET /api/v1/ldap/entities/:type — list entities of a type func (h *LDAPEntityHandler) List(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) typeName := c.Params("type") search := c.Query("search") limit := c.QueryInt("limit", 50) if limit > 500 { limit = 500 } if h.registry.GetEntityType(typeName) == nil { apiErr := types.NewBadRequest("Unknown entity type: " + typeName) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } entities, err := h.svc.ListEntities(typeName, search, limit) if err != nil { apiErr := classifyAPIError(err) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } return c.JSON(types.NewPagedResponse(entities, int64(len(entities)), limit, 0, reqID)) } // Get handles GET /api/v1/ldap/entities/:type/:rdn — get a single entity func (h *LDAPEntityHandler) Get(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) typeName := c.Params("type") rdn := c.Params("rdn") if h.registry.GetEntityType(typeName) == nil { apiErr := types.NewBadRequest("Unknown entity type: " + typeName) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } entity, err := h.svc.GetEntity(typeName, rdn) if err != nil { apiErr := classifyAPIError(err) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } if entity == nil { apiErr := types.NewNotFound("Entity not found: " + rdn) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } return c.JSON(types.NewDataResponse(entity, reqID)) } // Create handles POST /api/v1/ldap/entities/:type — create an entity func (h *LDAPEntityHandler) Create(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) typeName := c.Params("type") if h.registry.GetEntityType(typeName) == nil { apiErr := types.NewBadRequest("Unknown entity type: " + typeName) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } var req types.LDAPEntityCreate if err := c.BodyParser(&req); err != nil { apiErr := types.NewBadRequest("Invalid request body: " + err.Error()) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } if len(req.Attributes) == 0 { apiErr := types.NewValidation("attributes are required") return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } entity, err := h.svc.CreateEntity(typeName, &req) if err != nil { apiErr := classifyAPIError(err) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } return c.Status(fiber.StatusCreated).JSON(types.NewDataResponse(entity, reqID)) } // Update handles PUT /api/v1/ldap/entities/:type/:rdn — update an entity func (h *LDAPEntityHandler) Update(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) typeName := c.Params("type") rdn := c.Params("rdn") if h.registry.GetEntityType(typeName) == nil { apiErr := types.NewBadRequest("Unknown entity type: " + typeName) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } var req types.LDAPEntityUpdate if err := c.BodyParser(&req); err != nil { apiErr := types.NewBadRequest("Invalid request body: " + err.Error()) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } entity, err := h.svc.UpdateEntity(typeName, rdn, &req) if err != nil { apiErr := classifyAPIError(err) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } return c.JSON(types.NewDataResponse(entity, reqID)) } // Delete handles DELETE /api/v1/ldap/entities/:type/:rdn — delete an entity func (h *LDAPEntityHandler) Delete(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) typeName := c.Params("type") rdn := c.Params("rdn") if h.registry.GetEntityType(typeName) == nil { apiErr := types.NewBadRequest("Unknown entity type: " + typeName) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } if err := h.svc.DeleteEntity(typeName, rdn); err != nil { apiErr := classifyAPIError(err) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } return c.JSON(types.NewDataResponse(fiber.Map{"type": typeName, "rdn": rdn, "deleted": true}, reqID)) } // classifyAPIError maps service errors to appropriate HTTP error responses func classifyAPIError(err error) *types.APIError { kind, msg := service.ClassifyError(err) switch kind { case "conflict": return types.NewConflict(msg) case "not_found": return types.NewNotFound(msg) case "validation": return types.NewValidation(msg) default: return types.NewInternal(msg) } }