package db import ( "bytes" "crypto/rand" "encoding/base64" "errors" "fmt" "time" "github.com/google/uuid" "golang.org/x/crypto/argon2" "gorm.io/gorm" ) type User struct { ID uint `gorm:"primaryKey"` User string `gorm:"uniqueIndex"` Password string Admin bool CreatedAt time.Time UpdatedAt time.Time DeletedAt gorm.DeletedAt } func NewUser(db *gorm.DB, username string, password string, admin bool) error { // Generate a new UUID for the user id, err := uuid.NewRandom() if err != nil { return err } // Hash the password using argon2id hashedPassword, err := HashPassword(password) if err != nil { return err } // Create a new user record user := &User{ ID: uint(id.ID()), User: username, Password: hashedPassword, Admin: admin, } return db.Create(user).Error } func Authenticate(db *gorm.DB, username string, password string) (*User, error) { // Get the user record by username var user User if err := db.Where("user = ?", username).First(&user).Error; err != nil { return nil, errors.New("user not found") } // Compare the hashed password with the provided password if !comparePassword(user.Password, password) { return nil, errors.New("invalid password") } return &user, nil } func HashPassword(password string) (string, error) { // Generate a salt for the password hash salt := make([]byte, 16) if _, err := rand.Read(salt); err != nil { return "", err } // Hash the password using argon2id hash := argon2.IDKey([]byte(password), salt, 2, 64*1024, 1, 64) // Encode the salt and hash as a string return string(salt) + string(hash), nil } func comparePassword(hashedPassword string, password string) bool { // Decode the salt and hash from the hashed password string salt := []byte(hashedPassword)[:16] hash := []byte(hashedPassword)[16:] // Hash the password using the same salt and parameters computedHash := argon2.IDKey([]byte(password), salt, 2, 64*1024, 1, 64) // Compare the computed hash with the stored hash return bytes.Equal(hash, computedHash) } // DeleteUser deletes a user with the specified username from the database. func DeleteUser(db *gorm.DB, username string) error { // Find the user by username var user User if err := db.Where("user = ?", username).First(&user).Error; err != nil { return err } // Delete the user if err := db.Delete(&user).Error; err != nil { return err } return nil } func ChangeUser(db *gorm.DB, username string, updatedUser *User) error { // Retrieve the existing user var existingUser User if err := db.Where("user = ?", username).First(&existingUser).Error; err != nil { return err } // Update the user fields existingUser.User = updatedUser.User existingUser.Password = updatedUser.Password existingUser.Admin = updatedUser.Admin // Save the updated user to the database if err := db.Save(&existingUser).Error; err != nil { return err } return nil } func CreateAdminUser(db *gorm.DB, adminUsername string) error { // Check if the admin user already exists var admins []User if err := db.Unscoped().Limit(1).Where("user = ?", adminUsername).Find(&admins).Error; err != nil { return err } if len(admins) > 0 { // already exists return nil } // Generate a random 24-char password passwordBytes := make([]byte, 24) if _, err := rand.Read(passwordBytes); err != nil { return err } password := base64.URLEncoding.EncodeToString(passwordBytes) // Create the admin user if err := NewUser(db, adminUsername, password, true); err != nil { return err } // Print out the generated password fmt.Printf("Generated password for admin user %s: %s\n", adminUsername, password) return nil }