monorepo/cloud/maplefile-backend/pkg/validation/email.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 "***"
}