package user import ( "errors" "regexp" "time" ) // User represents a user entity in the domain // Every user strictly belongs to a tenant type User struct { ID string Email string FirstName string LastName string Name string LexicalName string Timezone string // Role management Role int // State management Status int // Embedded structs for better organization ProfileData *UserProfileData // Encapsulating security related data SecurityData *UserSecurityData // Metadata about the user Metadata *UserMetadata // Limited metadata fields used for querying TenantID string // Every user belongs to a tenant CreatedAt time.Time UpdatedAt time.Time } // UserProfileData contains user profile information type UserProfileData struct { Phone string Country string Region string City string PostalCode string AddressLine1 string AddressLine2 string HasShippingAddress bool ShippingName string ShippingPhone string ShippingCountry string ShippingRegion string ShippingCity string ShippingPostalCode string ShippingAddressLine1 string ShippingAddressLine2 string Timezone string AgreeTermsOfService bool AgreePromotions bool AgreeToTrackingAcrossThirdPartyAppsAndServices bool } // UserMetadata contains audit and tracking information type UserMetadata struct { // CWE-359: Encrypted IP addresses for GDPR compliance CreatedFromIPAddress string // Encrypted with go-ipcrypt CreatedFromIPTimestamp time.Time // For 90-day expiration tracking CreatedByUserID string CreatedAt time.Time CreatedByName string ModifiedFromIPAddress string // Encrypted with go-ipcrypt ModifiedFromIPTimestamp time.Time // For 90-day expiration tracking ModifiedByUserID string ModifiedAt time.Time ModifiedByName string LastLoginAt time.Time } // FullName returns the user's full name computed from FirstName and LastName func (u *User) FullName() string { if u.FirstName == "" && u.LastName == "" { return u.Name // Fallback to Name field if first/last are empty } return u.FirstName + " " + u.LastName } // UserSecurityData contains security-related information type UserSecurityData struct { PasswordHashAlgorithm string PasswordHash string WasEmailVerified bool Code string CodeType string // 'email_verification' or 'password_reset' CodeExpiry time.Time // OTPEnabled controls whether we force 2FA or not during login OTPEnabled bool // OTPVerified indicates user has successfully validated their OTP token after enabling 2FA OTPVerified bool // OTPValidated automatically gets set as `false` on successful login and then sets `true` once successfully validated by 2FA OTPValidated bool // OTPSecret the unique one-time password secret to be shared between our backend and 2FA authenticator apps OTPSecret string // OTPAuthURL is the URL used to share OTPAuthURL string // OTPBackupCodeHash is the one-time use backup code which resets the 2FA settings OTPBackupCodeHash string // OTPBackupCodeHashAlgorithm tracks the hashing algorithm used OTPBackupCodeHashAlgorithm string } // Domain errors var ( ErrUserNotFound = errors.New("user not found") ErrInvalidEmail = errors.New("invalid email format") ErrEmailRequired = errors.New("email is required") ErrFirstNameRequired = errors.New("first name is required") ErrLastNameRequired = errors.New("last name is required") ErrNameRequired = errors.New("name is required") ErrTenantIDRequired = errors.New("tenant ID is required") ErrPasswordRequired = errors.New("password is required") ErrPasswordTooShort = errors.New("password must be at least 8 characters") ErrPasswordTooWeak = errors.New("password must contain uppercase, lowercase, number, and special character") ErrRoleRequired = errors.New("role is required") ErrUserAlreadyExists = errors.New("user already exists") ErrInvalidCredentials = errors.New("invalid credentials") ErrTermsOfServiceRequired = errors.New("must agree to terms of service") ) // Email validation regex (basic) var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`) // Validate validates the user entity func (u *User) Validate() error { if u.TenantID == "" { return ErrTenantIDRequired } if u.Email == "" { return ErrEmailRequired } if !emailRegex.MatchString(u.Email) { return ErrInvalidEmail } if u.Name == "" { return ErrNameRequired } // Validate ProfileData if present if u.ProfileData != nil { // Terms of Service is REQUIRED if !u.ProfileData.AgreeTermsOfService { return ErrTermsOfServiceRequired } } return nil }