This commit is contained in:
2025-08-23 17:22:37 -04:00
parent 632473a7d8
commit d659a47764
6 changed files with 797 additions and 55 deletions

View File

@ -2,8 +2,11 @@ package services
import (
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"github.com/go-playground/validator/v10"
"go.uber.org/zap"
"github.com/kms/api-key-service/internal/domain"
@ -12,15 +15,17 @@ import (
// applicationService implements the ApplicationService interface
type applicationService struct {
appRepo repository.ApplicationRepository
logger *zap.Logger
appRepo repository.ApplicationRepository
logger *zap.Logger
validator *validator.Validate
}
// NewApplicationService creates a new application service
func NewApplicationService(appRepo repository.ApplicationRepository, logger *zap.Logger) ApplicationService {
return &applicationService{
appRepo: appRepo,
logger: logger,
appRepo: appRepo,
logger: logger,
validator: validator.New(),
}
}
@ -28,15 +33,32 @@ func NewApplicationService(appRepo repository.ApplicationRepository, logger *zap
func (s *applicationService) Create(ctx context.Context, req *domain.CreateApplicationRequest, userID string) (*domain.Application, error) {
s.logger.Info("Creating application", zap.String("app_id", req.AppID), zap.String("user_id", userID))
// TODO: Add permission validation
// TODO: Add input validation using validator
// Input validation using validator
if err := s.validator.Struct(req); err != nil {
s.logger.Warn("Application creation request validation failed",
zap.String("app_id", req.AppID),
zap.String("user_id", userID),
zap.Error(err))
return nil, fmt.Errorf("validation failed: %w", err)
}
// Basic permission validation - check if user can create applications
// In a real system, this would check against user roles/permissions
if userID == "" {
return nil, fmt.Errorf("user authentication required")
}
// Additional business logic validation
if req.TokenRenewalDuration > req.MaxTokenDuration {
return nil, fmt.Errorf("token renewal duration cannot be greater than max token duration")
}
app := &domain.Application{
AppID: req.AppID,
AppLink: req.AppLink,
Type: req.Type,
CallbackURL: req.CallbackURL,
HMACKey: generateHMACKey(), // TODO: Use proper key generation
HMACKey: generateHMACKey(), // Uses crypto/rand for secure key generation
TokenPrefix: req.TokenPrefix,
TokenRenewalDuration: req.TokenRenewalDuration,
MaxTokenDuration: req.MaxTokenDuration,
@ -90,8 +112,27 @@ func (s *applicationService) List(ctx context.Context, limit, offset int) ([]*do
func (s *applicationService) Update(ctx context.Context, appID string, updates *domain.UpdateApplicationRequest, userID string) (*domain.Application, error) {
s.logger.Info("Updating application", zap.String("app_id", appID), zap.String("user_id", userID))
// TODO: Add permission validation
// TODO: Add input validation
// Input validation using validator
if err := s.validator.Struct(updates); err != nil {
s.logger.Warn("Application update request validation failed",
zap.String("app_id", appID),
zap.String("user_id", userID),
zap.Error(err))
return nil, fmt.Errorf("validation failed: %w", err)
}
// Basic permission validation - check if user can update applications
// In a real system, this would check against user roles/permissions and application ownership
if userID == "" {
return nil, fmt.Errorf("user authentication required")
}
// Additional business logic validation
if updates.TokenRenewalDuration != nil && updates.MaxTokenDuration != nil {
if *updates.TokenRenewalDuration > *updates.MaxTokenDuration {
return nil, fmt.Errorf("token renewal duration cannot be greater than max token duration")
}
}
app, err := s.appRepo.Update(ctx, appID, updates)
if err != nil {
@ -107,8 +148,36 @@ func (s *applicationService) Update(ctx context.Context, appID string, updates *
func (s *applicationService) Delete(ctx context.Context, appID string, userID string) error {
s.logger.Info("Deleting application", zap.String("app_id", appID), zap.String("user_id", userID))
// TODO: Add permission validation
// TODO: Check for existing tokens and handle appropriately
// Basic permission validation - check if user can delete applications
// In a real system, this would check against user roles/permissions and application ownership
if userID == "" {
return fmt.Errorf("user authentication required")
}
// Input validation - check appID format
if appID == "" {
return fmt.Errorf("application ID is required")
}
// Check if application exists before attempting deletion
_, err := s.appRepo.GetByID(ctx, appID)
if err != nil {
s.logger.Warn("Application not found for deletion",
zap.String("app_id", appID),
zap.String("user_id", userID))
return fmt.Errorf("application not found: %w", err)
}
// Check for existing tokens and handle appropriately
// In a production system, we would implement one of these strategies:
// 1. Prevent deletion if active tokens exist (safe approach)
// 2. Cascade delete all associated tokens and permissions (clean approach)
// 3. Mark application as deleted but keep tokens active until they expire
// For now, log a warning about potential orphaned tokens
s.logger.Warn("Application deletion will proceed without checking for existing tokens",
zap.String("app_id", appID),
zap.String("recommendation", "implement token cleanup or prevention logic"))
if err := s.appRepo.Delete(ctx, appID); err != nil {
s.logger.Error("Failed to delete application", zap.Error(err), zap.String("app_id", appID))
@ -120,8 +189,15 @@ func (s *applicationService) Delete(ctx context.Context, appID string, userID st
}
// generateHMACKey generates a secure HMAC key
// TODO: Replace with proper cryptographic key generation
func generateHMACKey() string {
// This is a placeholder - should use proper crypto/rand
return "generated-hmac-key-placeholder"
// Generate 32 bytes (256 bits) of cryptographically secure random data
key := make([]byte, 32)
_, err := rand.Read(key)
if err != nil {
// If we can't generate random bytes, this is a critical security issue
panic(fmt.Sprintf("Failed to generate cryptographic key: %v", err))
}
// Return as hex-encoded string for storage
return hex.EncodeToString(key)
}