Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
169
cloud/maplepress-backend/internal/domain/user/entity.go
Normal file
169
cloud/maplepress-backend/internal/domain/user/entity.go
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue