rwadurian/backend/mpc-system/services/account/domain/entities/account.go

157 lines
4.4 KiB
Go

package entities
import (
"time"
"github.com/google/uuid"
"github.com/rwadurian/mpc-system/services/account/domain/value_objects"
)
// Account represents a user account with MPC-based authentication
type Account struct {
ID value_objects.AccountID
Username string
Email string
Phone *string
PublicKey []byte // MPC group public key
KeygenSessionID uuid.UUID
ThresholdN int
ThresholdT int
Status value_objects.AccountStatus
CreatedAt time.Time
UpdatedAt time.Time
LastLoginAt *time.Time
}
// NewAccount creates a new Account
func NewAccount(
username string,
email string,
publicKey []byte,
keygenSessionID uuid.UUID,
thresholdN int,
thresholdT int,
) *Account {
now := time.Now().UTC()
return &Account{
ID: value_objects.NewAccountID(),
Username: username,
Email: email,
PublicKey: publicKey,
KeygenSessionID: keygenSessionID,
ThresholdN: thresholdN,
ThresholdT: thresholdT,
Status: value_objects.AccountStatusActive,
CreatedAt: now,
UpdatedAt: now,
}
}
// SetPhone sets the phone number
func (a *Account) SetPhone(phone string) {
a.Phone = &phone
a.UpdatedAt = time.Now().UTC()
}
// UpdateLastLogin updates the last login timestamp
func (a *Account) UpdateLastLogin() {
now := time.Now().UTC()
a.LastLoginAt = &now
a.UpdatedAt = now
}
// Suspend suspends the account
func (a *Account) Suspend() error {
if a.Status == value_objects.AccountStatusRecovering {
return ErrAccountInRecovery
}
a.Status = value_objects.AccountStatusSuspended
a.UpdatedAt = time.Now().UTC()
return nil
}
// Lock locks the account
func (a *Account) Lock() error {
if a.Status == value_objects.AccountStatusRecovering {
return ErrAccountInRecovery
}
a.Status = value_objects.AccountStatusLocked
a.UpdatedAt = time.Now().UTC()
return nil
}
// Activate activates the account
func (a *Account) Activate() {
a.Status = value_objects.AccountStatusActive
a.UpdatedAt = time.Now().UTC()
}
// StartRecovery marks the account as recovering
func (a *Account) StartRecovery() error {
if !a.Status.CanInitiateRecovery() {
return ErrCannotInitiateRecovery
}
a.Status = value_objects.AccountStatusRecovering
a.UpdatedAt = time.Now().UTC()
return nil
}
// CompleteRecovery completes the recovery process with new public key
func (a *Account) CompleteRecovery(newPublicKey []byte, newKeygenSessionID uuid.UUID) {
a.PublicKey = newPublicKey
a.KeygenSessionID = newKeygenSessionID
a.Status = value_objects.AccountStatusActive
a.UpdatedAt = time.Now().UTC()
}
// CanLogin checks if the account can login
func (a *Account) CanLogin() bool {
return a.Status.CanLogin()
}
// IsActive checks if the account is active
func (a *Account) IsActive() bool {
return a.Status == value_objects.AccountStatusActive
}
// Validate validates the account data
func (a *Account) Validate() error {
if a.Username == "" {
return ErrInvalidUsername
}
if a.Email == "" {
return ErrInvalidEmail
}
if len(a.PublicKey) == 0 {
return ErrInvalidPublicKey
}
if a.ThresholdT > a.ThresholdN || a.ThresholdT <= 0 {
return ErrInvalidThreshold
}
return nil
}
// Account errors
var (
ErrInvalidUsername = &AccountError{Code: "INVALID_USERNAME", Message: "username is required"}
ErrInvalidEmail = &AccountError{Code: "INVALID_EMAIL", Message: "email is required"}
ErrInvalidPublicKey = &AccountError{Code: "INVALID_PUBLIC_KEY", Message: "public key is required"}
ErrInvalidThreshold = &AccountError{Code: "INVALID_THRESHOLD", Message: "invalid threshold configuration"}
ErrAccountInRecovery = &AccountError{Code: "ACCOUNT_IN_RECOVERY", Message: "account is in recovery mode"}
ErrCannotInitiateRecovery = &AccountError{Code: "CANNOT_INITIATE_RECOVERY", Message: "cannot initiate recovery in current state"}
ErrAccountNotActive = &AccountError{Code: "ACCOUNT_NOT_ACTIVE", Message: "account is not active"}
ErrAccountNotFound = &AccountError{Code: "ACCOUNT_NOT_FOUND", Message: "account not found"}
ErrDuplicateUsername = &AccountError{Code: "DUPLICATE_USERNAME", Message: "username already exists"}
ErrDuplicateEmail = &AccountError{Code: "DUPLICATE_EMAIL", Message: "email already exists"}
)
// AccountError represents an account domain error
type AccountError struct {
Code string
Message string
}
func (e *AccountError) Error() string {
return e.Message
}