232 lines
6.6 KiB
Go
232 lines
6.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/kms/api-key-service/internal/domain"
|
|
"github.com/kms/api-key-service/internal/errors"
|
|
"github.com/kms/api-key-service/internal/services"
|
|
"github.com/kms/api-key-service/internal/validation"
|
|
)
|
|
|
|
// TokenHandler handles token-related HTTP requests
|
|
type TokenHandler struct {
|
|
tokenService services.TokenService
|
|
authService services.AuthenticationService
|
|
validator *validation.Validator
|
|
errorHandler *errors.ErrorHandler
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewTokenHandler creates a new token handler
|
|
func NewTokenHandler(
|
|
tokenService services.TokenService,
|
|
authService services.AuthenticationService,
|
|
logger *zap.Logger,
|
|
) *TokenHandler {
|
|
return &TokenHandler{
|
|
tokenService: tokenService,
|
|
authService: authService,
|
|
validator: validation.NewValidator(logger),
|
|
errorHandler: errors.NewErrorHandler(logger),
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// Create handles POST /applications/:id/tokens
|
|
func (h *TokenHandler) Create(c *gin.Context) {
|
|
// Validate application ID parameter
|
|
appID := c.Param("id")
|
|
if appID == "" {
|
|
h.errorHandler.HandleValidationError(c, "id", "Application ID is required")
|
|
return
|
|
}
|
|
|
|
// Bind and validate JSON request
|
|
var req domain.CreateStaticTokenRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
h.logger.Warn("Invalid request body", zap.Error(err))
|
|
h.errorHandler.HandleValidationError(c, "request_body", "Invalid request body format")
|
|
return
|
|
}
|
|
|
|
// Set app ID from URL parameter
|
|
req.AppID = appID
|
|
|
|
// Basic validation - the service layer will do more comprehensive validation
|
|
if req.AppID == "" {
|
|
h.errorHandler.HandleValidationError(c, "app_id", "Application ID is required")
|
|
return
|
|
}
|
|
|
|
// Get user ID from context
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
h.logger.Error("User ID not found in context")
|
|
h.errorHandler.HandleAuthenticationError(c, errors.NewAuthenticationError("Authentication context not found"))
|
|
return
|
|
}
|
|
|
|
userIDStr, ok := userID.(string)
|
|
if !ok {
|
|
h.logger.Error("Invalid user ID type in context", zap.Any("user_id", userID))
|
|
h.errorHandler.HandleInternalError(c, errors.NewInternalError("Invalid authentication context"))
|
|
return
|
|
}
|
|
|
|
// Create the token
|
|
token, err := h.tokenService.CreateStaticToken(c.Request.Context(), &req, userIDStr)
|
|
if err != nil {
|
|
h.logger.Error("Failed to create token",
|
|
zap.Error(err),
|
|
zap.String("app_id", appID),
|
|
zap.String("user_id", userIDStr))
|
|
|
|
// Handle different types of errors appropriately
|
|
if errors.IsNotFound(err) {
|
|
h.errorHandler.HandleError(c, err, "Application not found")
|
|
} else if errors.IsValidationError(err) {
|
|
h.errorHandler.HandleValidationError(c, "token", "Token creation validation failed")
|
|
} else if errors.IsAuthorizationError(err) {
|
|
h.errorHandler.HandleAuthorizationError(c, "token_creation")
|
|
} else {
|
|
h.errorHandler.HandleInternalError(c, err)
|
|
}
|
|
return
|
|
}
|
|
|
|
h.logger.Info("Token created successfully",
|
|
zap.String("token_id", token.ID.String()),
|
|
zap.String("app_id", appID),
|
|
zap.String("user_id", userIDStr))
|
|
|
|
c.JSON(http.StatusCreated, token)
|
|
}
|
|
|
|
// ListByApp handles GET /applications/:id/tokens
|
|
func (h *TokenHandler) ListByApp(c *gin.Context) {
|
|
// Validate application ID parameter
|
|
appID := c.Param("id")
|
|
if appID == "" {
|
|
h.errorHandler.HandleValidationError(c, "id", "Application ID is required")
|
|
return
|
|
}
|
|
|
|
// Parse and validate pagination parameters
|
|
limit := 50
|
|
offset := 0
|
|
|
|
if l := c.Query("limit"); l != "" {
|
|
if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 && parsed <= 1000 {
|
|
limit = parsed
|
|
} else if parsed <= 0 || parsed > 1000 {
|
|
h.errorHandler.HandleValidationError(c, "limit", "Limit must be between 1 and 1000")
|
|
return
|
|
}
|
|
}
|
|
|
|
if o := c.Query("offset"); o != "" {
|
|
if parsed, err := strconv.Atoi(o); err == nil && parsed >= 0 {
|
|
offset = parsed
|
|
} else if parsed < 0 {
|
|
h.errorHandler.HandleValidationError(c, "offset", "Offset must be non-negative")
|
|
return
|
|
}
|
|
}
|
|
|
|
// List tokens
|
|
tokens, err := h.tokenService.ListByApp(c.Request.Context(), appID, limit, offset)
|
|
if err != nil {
|
|
h.logger.Error("Failed to list tokens",
|
|
zap.Error(err),
|
|
zap.String("app_id", appID),
|
|
zap.Int("limit", limit),
|
|
zap.Int("offset", offset))
|
|
|
|
// Handle different types of errors appropriately
|
|
if errors.IsNotFound(err) {
|
|
h.errorHandler.HandleNotFoundError(c, "application", "Application not found")
|
|
} else if errors.IsAuthorizationError(err) {
|
|
h.errorHandler.HandleAuthorizationError(c, "token_list")
|
|
} else {
|
|
h.errorHandler.HandleInternalError(c, err)
|
|
}
|
|
return
|
|
}
|
|
|
|
h.logger.Debug("Tokens listed successfully",
|
|
zap.String("app_id", appID),
|
|
zap.Int("token_count", len(tokens)),
|
|
zap.Int("limit", limit),
|
|
zap.Int("offset", offset))
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"data": tokens,
|
|
"limit": limit,
|
|
"offset": offset,
|
|
"count": len(tokens),
|
|
})
|
|
}
|
|
|
|
// Delete handles DELETE /tokens/:id
|
|
func (h *TokenHandler) Delete(c *gin.Context) {
|
|
// Validate token ID parameter
|
|
tokenIDStr := c.Param("id")
|
|
if tokenIDStr == "" {
|
|
h.errorHandler.HandleValidationError(c, "id", "Token ID is required")
|
|
return
|
|
}
|
|
|
|
tokenID, err := uuid.Parse(tokenIDStr)
|
|
if err != nil {
|
|
h.logger.Warn("Invalid token ID format", zap.String("token_id", tokenIDStr), zap.Error(err))
|
|
h.errorHandler.HandleValidationError(c, "id", "Invalid token ID format")
|
|
return
|
|
}
|
|
|
|
// Get user ID from context
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
h.logger.Error("User ID not found in context")
|
|
h.errorHandler.HandleAuthenticationError(c, errors.NewAuthenticationError("Authentication context not found"))
|
|
return
|
|
}
|
|
|
|
userIDStr, ok := userID.(string)
|
|
if !ok {
|
|
h.logger.Error("Invalid user ID type in context", zap.Any("user_id", userID))
|
|
h.errorHandler.HandleInternalError(c, errors.NewInternalError("Invalid authentication context"))
|
|
return
|
|
}
|
|
|
|
// Delete the token
|
|
err = h.tokenService.Delete(c.Request.Context(), tokenID, userIDStr)
|
|
if err != nil {
|
|
h.logger.Error("Failed to delete token",
|
|
zap.Error(err),
|
|
zap.String("token_id", tokenID.String()),
|
|
zap.String("user_id", userIDStr))
|
|
|
|
// Handle different types of errors appropriately
|
|
if errors.IsNotFound(err) {
|
|
h.errorHandler.HandleNotFoundError(c, "token", "Token not found")
|
|
} else if errors.IsAuthorizationError(err) {
|
|
h.errorHandler.HandleAuthorizationError(c, "token_deletion")
|
|
} else {
|
|
h.errorHandler.HandleInternalError(c, err)
|
|
}
|
|
return
|
|
}
|
|
|
|
h.logger.Info("Token deleted successfully",
|
|
zap.String("token_id", tokenID.String()),
|
|
zap.String("user_id", userIDStr))
|
|
|
|
c.JSON(http.StatusNoContent, nil)
|
|
}
|