105 lines
2.6 KiB
Go
105 lines
2.6 KiB
Go
// Package validation provides shared input validation utilities.
|
|
package validation
|
|
|
|
import (
|
|
"net"
|
|
"net/mail"
|
|
"strings"
|
|
)
|
|
|
|
// ValidateEmail validates an email address using Go's standard mail package.
|
|
// Returns an error message if the email is invalid, or an empty string if valid.
|
|
// This provides consistent email validation across all services.
|
|
func ValidateEmail(email string) string {
|
|
email = strings.TrimSpace(email)
|
|
if email == "" {
|
|
return "Email address is required"
|
|
}
|
|
|
|
// Use Go's mail package for proper RFC 5322 email validation
|
|
if _, err := mail.ParseAddress(email); err != nil {
|
|
return "Please enter a valid email address"
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// IsValidEmail returns true if the email is valid, false otherwise.
|
|
// This is a convenience wrapper around ValidateEmail for simple boolean checks.
|
|
func IsValidEmail(email string) bool {
|
|
return ValidateEmail(email) == ""
|
|
}
|
|
|
|
// MaskEmail masks an email address for safe logging.
|
|
// Example: "john.doe@example.com" becomes "jo***@example.com"
|
|
// This prevents PII exposure in logs while still allowing identification.
|
|
func MaskEmail(email string) string {
|
|
email = strings.TrimSpace(email)
|
|
if email == "" {
|
|
return "***"
|
|
}
|
|
|
|
parts := strings.Split(email, "@")
|
|
if len(parts) != 2 {
|
|
return "***"
|
|
}
|
|
|
|
localPart := parts[0]
|
|
domain := parts[1]
|
|
|
|
// Mask local part based on length
|
|
var maskedLocal string
|
|
switch {
|
|
case len(localPart) <= 1:
|
|
maskedLocal = "*"
|
|
case len(localPart) <= 3:
|
|
maskedLocal = localPart[:1] + "***"
|
|
default:
|
|
maskedLocal = localPart[:2] + "***"
|
|
}
|
|
|
|
return maskedLocal + "@" + domain
|
|
}
|
|
|
|
// MaskIP masks an IP address for safe logging.
|
|
// IPv4 example: "192.168.1.100" becomes "192.168.1.***"
|
|
// IPv6 example: "2001:db8::1" becomes "2001:db8::***"
|
|
// This prevents PII exposure in logs while still allowing network identification.
|
|
func MaskIP(ip string) string {
|
|
ip = strings.TrimSpace(ip)
|
|
if ip == "" {
|
|
return "***"
|
|
}
|
|
|
|
// Remove port if present (format: "IP:port" or "[IPv6]:port")
|
|
host, _, err := net.SplitHostPort(ip)
|
|
if err == nil {
|
|
ip = host
|
|
}
|
|
|
|
// Parse to determine if IPv4 or IPv6
|
|
parsedIP := net.ParseIP(ip)
|
|
if parsedIP == nil {
|
|
return "***"
|
|
}
|
|
|
|
// Check if IPv4
|
|
if ipv4 := parsedIP.To4(); ipv4 != nil {
|
|
// Mask last octet: 192.168.1.100 -> 192.168.1.***
|
|
parts := strings.Split(ip, ".")
|
|
if len(parts) == 4 {
|
|
return parts[0] + "." + parts[1] + "." + parts[2] + ".***"
|
|
}
|
|
return "***"
|
|
}
|
|
|
|
// IPv6: mask last segment
|
|
// Example: 2001:db8:85a3::8a2e:370:7334 -> 2001:db8:85a3::8a2e:370:***
|
|
parts := strings.Split(ip, ":")
|
|
if len(parts) > 1 {
|
|
parts[len(parts)-1] = "***"
|
|
return strings.Join(parts, ":")
|
|
}
|
|
|
|
return "***"
|
|
}
|