package tenant import ( "errors" "regexp" "time" ) var ( ErrNameRequired = errors.New("tenant name is required") ErrNameTooShort = errors.New("tenant name must be at least 2 characters") ErrNameTooLong = errors.New("tenant name must not exceed 100 characters") ErrSlugRequired = errors.New("tenant slug is required") ErrSlugInvalid = errors.New("tenant slug must contain only lowercase letters, numbers, and hyphens") ErrTenantNotFound = errors.New("tenant not found") ErrTenantExists = errors.New("tenant already exists") ErrTenantInactive = errors.New("tenant is inactive") ) // Status represents the tenant's current status type Status string const ( StatusActive Status = "active" StatusInactive Status = "inactive" StatusSuspended Status = "suspended" ) // Tenant represents a tenant in the system // Each tenant is a separate customer/organization type Tenant struct { ID string Name string // Display name (e.g., "Acme Corporation") Slug string // URL-friendly identifier (e.g., "acme-corp") Status Status CreatedAt time.Time UpdatedAt time.Time // CWE-359: IP address tracking for GDPR compliance (90-day expiration) CreatedFromIPAddress string // Encrypted IP address CreatedFromIPTimestamp time.Time // For 90-day expiration tracking ModifiedFromIPAddress string // Encrypted IP address ModifiedFromIPTimestamp time.Time // For 90-day expiration tracking } var slugRegex = regexp.MustCompile(`^[a-z0-9]+(?:-[a-z0-9]+)*$`) // Validate validates the tenant entity func (t *Tenant) Validate() error { // Name validation if t.Name == "" { return ErrNameRequired } if len(t.Name) < 2 { return ErrNameTooShort } if len(t.Name) > 100 { return ErrNameTooLong } // Slug validation if t.Slug == "" { return ErrSlugRequired } if !slugRegex.MatchString(t.Slug) { return ErrSlugInvalid } return nil } // IsActive returns true if the tenant is active func (t *Tenant) IsActive() bool { return t.Status == StatusActive }