Files

184 lines
5.2 KiB
Go

// 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))
}
}