971a2bd9a9
- Add rate limiting per endpoint (login: 5/min, API: 100/min) - Add input validation helpers (email, UUID, string, int) - Add tenant isolation to all handlers - Remove old validation.go, replace with input.go - Fix service/customer.go to use new validation functions - Build successful
133 lines
2.8 KiB
Go
133 lines
2.8 KiB
Go
package middleware
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
// Email regex
|
|
emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
|
|
|
|
// UUID regex
|
|
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}$`)
|
|
|
|
// Safe string regex (tillåtna tecken)
|
|
safeStringRegex = regexp.MustCompile(`^[a-zA-Z0-9\s\-_\.@,;:()\[\]{}]+$`)
|
|
)
|
|
|
|
// ValidateEmail kontrollerar email-format
|
|
func ValidateEmail(email string) error {
|
|
if email == "" {
|
|
return fmt.Errorf("email is required")
|
|
}
|
|
if len(email) > 254 {
|
|
return fmt.Errorf("email too long")
|
|
}
|
|
if !emailRegex.MatchString(email) {
|
|
return fmt.Errorf("invalid email format")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateUUID kontrollerar UUID-format
|
|
func ValidateUUID(id string) error {
|
|
if id == "" {
|
|
return fmt.Errorf("id is required")
|
|
}
|
|
if !uuidRegex.MatchString(id) {
|
|
return fmt.Errorf("invalid UUID format")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateString kontrollerar sträng-input
|
|
func ValidateString(s string, minLen, maxLen int, required bool) error {
|
|
if required && strings.TrimSpace(s) == "" {
|
|
return fmt.Errorf("field is required")
|
|
}
|
|
if s != "" {
|
|
if len(s) < minLen {
|
|
return fmt.Errorf("must be at least %d characters", minLen)
|
|
}
|
|
if len(s) > maxLen {
|
|
return fmt.Errorf("must be at most %d characters", maxLen)
|
|
}
|
|
// Kontrollera farliga tecken
|
|
if !safeStringRegex.MatchString(s) {
|
|
return fmt.Errorf("contains invalid characters")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateInt kontrollerar heltal
|
|
func ValidateInt(val int, min, max int, required bool) error {
|
|
if required && val == 0 {
|
|
return fmt.Errorf("field is required")
|
|
}
|
|
if val != 0 {
|
|
if val < min {
|
|
return fmt.Errorf("must be at least %d", min)
|
|
}
|
|
if val > max {
|
|
return fmt.Errorf("must be at most %d", max)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidatePagination kontrollerar pagination-parametrar
|
|
func ValidatePagination(page, limit int) (int, int, error) {
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
if limit < 1 {
|
|
limit = 20
|
|
}
|
|
if limit > 100 {
|
|
limit = 100
|
|
}
|
|
return page, limit, nil
|
|
}
|
|
|
|
// SanitizeString tar bort farliga tecken
|
|
func SanitizeString(s string) string {
|
|
// Ta bort null bytes
|
|
s = strings.ReplaceAll(s, "\x00", "")
|
|
// Ta bort kontrolltecken
|
|
var result strings.Builder
|
|
for _, r := range s {
|
|
if r >= 32 || r == '\t' || r == '\n' || r == '\r' {
|
|
result.WriteRune(r)
|
|
}
|
|
}
|
|
return strings.TrimSpace(result.String())
|
|
}
|
|
|
|
// ParseInt parse en sträng till heltal med validering
|
|
func ParseInt(s string, defaultVal int) int {
|
|
if s == "" {
|
|
return defaultVal
|
|
}
|
|
val, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return defaultVal
|
|
}
|
|
return val
|
|
}
|
|
|
|
// ParseFloat parse en sträng till float med validering
|
|
func ParseFloat(s string, defaultVal float64) float64 {
|
|
if s == "" {
|
|
return defaultVal
|
|
}
|
|
val, err := strconv.ParseFloat(s, 64)
|
|
if err != nil {
|
|
return defaultVal
|
|
}
|
|
return val
|
|
}
|