// Package validation provides centralized input validation package validation import ( "fmt" "regexp" "strings" ) // Validator provides validation functions type Validator struct{} // New creates a new Validator func New() *Validator { return &Validator{} } // ValidationError represents a validation error type ValidationError struct { Field string Message string } // ValidationResult contains validation results type ValidationResult struct { Valid bool Errors []ValidationError } // AddError adds an error to the result func (r *ValidationResult) AddError(field, message string) { r.Errors = append(r.Errors, ValidationError{Field: field, Message: message}) r.Valid = false } // NewResult creates a new valid result func NewResult() *ValidationResult { return &ValidationResult{Valid: true} } // Required validates that a string is not empty func (v *Validator) Required(value, field string, result *ValidationResult) { if strings.TrimSpace(value) == "" { result.AddError(field, fmt.Sprintf("%s is required", field)) } } // MinLength validates minimum string length func (v *Validator) MinLength(value, field string, min int, result *ValidationResult) { if len(value) < min { result.AddError(field, fmt.Sprintf("%s must be at least %d characters", field, min)) } } // MaxLength validates maximum string length func (v *Validator) MaxLength(value, field string, max int, result *ValidationResult) { if len(value) > max { result.AddError(field, fmt.Sprintf("%s must be at most %d characters", field, max)) } } // Email validates email format func (v *Validator) Email(value, field string, result *ValidationResult) { if value == "" { return // Skip if empty, use Required for mandatory } emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`) if !emailRegex.MatchString(value) { result.AddError(field, fmt.Sprintf("%s must be a valid email address", field)) } } // DN validates LDAP Distinguished Name format func (v *Validator) DN(value, field string, result *ValidationResult) { if value == "" { return } // Basic DN validation - must contain at least one RDN component if !strings.Contains(value, "=") { result.AddError(field, fmt.Sprintf("%s must be a valid Distinguished Name", field)) } } // CronExpression validates a cron expression (6 fields) func (v *Validator) CronExpression(value, field string, result *ValidationResult) { if value == "" { return } parts := strings.Fields(value) if len(parts) != 6 { result.AddError(field, fmt.Sprintf("%s must have 6 fields (second minute hour day month weekday)", field)) } } // Regex validates that a value matches a regex pattern func (v *Validator) Regex(value, pattern, field string, result *ValidationResult) { if value == "" { return } re, err := regexp.Compile(pattern) if err != nil { result.AddError(field, fmt.Sprintf("invalid validation pattern for %s", field)) return } if !re.MatchString(value) { result.AddError(field, fmt.Sprintf("%s has invalid format", field)) } } // ValidRegex validates that a string is a valid regex pattern func (v *Validator) ValidRegex(value, field string, result *ValidationResult) { if value == "" { return } _, err := regexp.Compile(value) if err != nil { result.AddError(field, fmt.Sprintf("%s must be a valid regular expression: %v", field, err)) } } // InList validates that a value is in a list of allowed values func (v *Validator) InList(value, field string, allowed []string, result *ValidationResult) { for _, a := range allowed { if value == a { return } } result.AddError(field, fmt.Sprintf("%s must be one of: %s", field, strings.Join(allowed, ", "))) } // Port validates a port number func (v *Validator) Port(value int, field string, result *ValidationResult) { if value < 1 || value > 65535 { result.AddError(field, fmt.Sprintf("%s must be between 1 and 65535", field)) } } // PositiveInt validates that an integer is positive func (v *Validator) PositiveInt(value int, field string, result *ValidationResult) { if value <= 0 { result.AddError(field, fmt.Sprintf("%s must be a positive number", field)) } } // UUID validates UUID format func (v *Validator) UUID(value, field string, result *ValidationResult) { if value == "" { return } uuidRegex := regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`) if !uuidRegex.MatchString(value) { result.AddError(field, fmt.Sprintf("%s must be a valid UUID", field)) } } // LDAPFilter validates basic LDAP filter syntax func (v *Validator) LDAPFilter(value, field string, result *ValidationResult) { if value == "" { return } // Basic validation - must start and end with parentheses value = strings.TrimSpace(value) if !strings.HasPrefix(value, "(") || !strings.HasSuffix(value, ")") { result.AddError(field, fmt.Sprintf("%s must be enclosed in parentheses", field)) return } // Check balanced parentheses count := 0 for _, c := range value { if c == '(' { count++ } else if c == ')' { count-- } if count < 0 { result.AddError(field, fmt.Sprintf("%s has unbalanced parentheses", field)) return } } if count != 0 { result.AddError(field, fmt.Sprintf("%s has unbalanced parentheses", field)) } }