mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-08 15:41:20 +02:00
167 lines
4.6 KiB
Go
167 lines
4.6 KiB
Go
package db
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// User represents a user account in the system
|
|
type User struct {
|
|
ID uint `gorm:"primarykey"`
|
|
Email string `gorm:"unique;not null"`
|
|
PasswordHash string `gorm:"not null"`
|
|
IsAdmin *bool `gorm:"default:false"`
|
|
LastPasswordChange time.Time
|
|
FailedLoginAttempts int `gorm:"default:0"`
|
|
AccountLocked *bool `gorm:"default:false"`
|
|
LockoutUntil *time.Time
|
|
Theme string `gorm:"default:'light'"`
|
|
TwoFactorSecret string `gorm:"type:varchar(32)"`
|
|
TwoFactorEnabled bool `gorm:"default:false"`
|
|
BackupCodes string `gorm:"type:text"` // Comma-separated backup codes
|
|
Roles []Role `gorm:"many2many:user_roles"`
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
// PasswordHistory stores previous passwords for a user
|
|
type PasswordHistory struct {
|
|
ID uint `gorm:"primarykey"`
|
|
UserID uint `gorm:"not null"`
|
|
User User `gorm:"foreignkey:UserID"`
|
|
PasswordHash string `gorm:"not null"`
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
// PasswordResetToken stores tokens for password reset requests
|
|
type PasswordResetToken struct {
|
|
ID uint `gorm:"primarykey"`
|
|
UserID uint `gorm:"not null"`
|
|
User User `gorm:"foreignkey:UserID"`
|
|
Token string `gorm:"not null"`
|
|
ExpiresAt time.Time `gorm:"not null"`
|
|
Used *bool `gorm:"default:false"`
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
// --- User Helper Methods ---
|
|
|
|
// GetIsAdmin returns the value of IsAdmin with a default if nil
|
|
func (u *User) GetIsAdmin() bool {
|
|
if u.IsAdmin == nil {
|
|
return false // Default to false if not set
|
|
}
|
|
return *u.IsAdmin
|
|
}
|
|
|
|
// SetIsAdmin sets the IsAdmin field
|
|
func (u *User) SetIsAdmin(value bool) {
|
|
u.IsAdmin = &value
|
|
}
|
|
|
|
// GetAccountLocked returns the value of AccountLocked with a default if nil
|
|
func (u *User) GetAccountLocked() bool {
|
|
if u.AccountLocked == nil {
|
|
return false // Default to false if not set
|
|
}
|
|
return *u.AccountLocked
|
|
}
|
|
|
|
// SetAccountLocked sets the AccountLocked field
|
|
func (u *User) SetAccountLocked(value bool) {
|
|
u.AccountLocked = &value
|
|
}
|
|
|
|
// HasRole checks if the user has a specific role
|
|
func (u *User) HasRole(roleName string) bool {
|
|
for _, role := range u.Roles {
|
|
if role.Name == roleName {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// HasPermission checks if the user has a specific permission through any of their roles
|
|
func (u *User) HasPermission(permission string) bool {
|
|
for _, role := range u.Roles {
|
|
if role.HasPermission(permission) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GetRoles returns all roles assigned to the user
|
|
// Note: This requires preloading Roles when fetching the user
|
|
func (u *User) GetRoles(tx *gorm.DB) ([]Role, error) {
|
|
var roles []Role
|
|
err := tx.Model(u).Association("Roles").Find(&roles)
|
|
return roles, err
|
|
}
|
|
|
|
// AssignRole assigns a role to the user
|
|
func (u *User) AssignRole(tx *gorm.DB, roleID uint, assignedByID uint) error {
|
|
var role Role
|
|
if err := tx.First(&role, roleID).Error; err != nil {
|
|
return err
|
|
}
|
|
// Assuming Role struct has AssignToUser method (from role.go)
|
|
return role.AssignToUser(tx, u.ID, assignedByID)
|
|
}
|
|
|
|
// UnassignRole removes a role from the user
|
|
func (u *User) UnassignRole(tx *gorm.DB, roleID uint, unassignedByID uint) error {
|
|
var role Role
|
|
if err := tx.First(&role, roleID).Error; err != nil {
|
|
return err
|
|
}
|
|
// Assuming Role struct has UnassignFromUser method (from role.go)
|
|
return role.UnassignFromUser(tx, u.ID, unassignedByID)
|
|
}
|
|
|
|
// SetPassword sets the user's password with secure hashing
|
|
func (u *User) SetPassword(password string) error {
|
|
// Validate password length
|
|
if len(password) < 8 {
|
|
return fmt.Errorf("password must be at least 8 characters long")
|
|
}
|
|
|
|
// Hash the password using bcrypt
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to hash password: %w", err)
|
|
}
|
|
|
|
// Store the hashed password
|
|
u.PasswordHash = string(hashedPassword)
|
|
u.LastPasswordChange = time.Now()
|
|
|
|
return nil
|
|
}
|
|
|
|
// CheckPassword verifies if the provided password matches the stored hash
|
|
func (u *User) CheckPassword(password string) bool {
|
|
err := bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(password))
|
|
return err == nil
|
|
}
|
|
|
|
// --- PasswordResetToken Helper Methods ---
|
|
|
|
// GetUsed returns the value of Used with a default if nil
|
|
func (t *PasswordResetToken) GetUsed() bool {
|
|
if t.Used == nil {
|
|
return false // Default to false if not set
|
|
}
|
|
return *t.Used
|
|
}
|
|
|
|
// SetUsed sets the Used field
|
|
func (t *PasswordResetToken) SetUsed(value bool) {
|
|
t.Used = &value
|
|
}
|