130 lines
2.9 KiB
Go
130 lines
2.9 KiB
Go
package models
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-sql-driver/mysql"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
type User struct {
|
|
ID int
|
|
Name string
|
|
Email string
|
|
HashedPassword []byte
|
|
Created time.Time
|
|
}
|
|
|
|
type UserModelInterface interface {
|
|
Insert(name, email, password string) error
|
|
Authenticate(email, password string) (int, error)
|
|
Exists(id int) (bool, error)
|
|
GetById(id int) (*User, error)
|
|
ChangePassword(id int, curr_pass, new_pass string) error
|
|
}
|
|
|
|
type UserModel struct {
|
|
DB *sql.DB
|
|
}
|
|
|
|
func (m *UserModel) Insert(name, email, password string) error {
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 12)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
stmt := `INSERT INTO users (name, email, hashed_password, created)
|
|
VALUES(?, ?, ?, UTC_TIMESTAMP())`
|
|
|
|
_, err = m.DB.Exec(stmt, name, email, string(hashedPassword))
|
|
if err != nil {
|
|
var mySQLError *mysql.MySQLError
|
|
if errors.As(err, &mySQLError) {
|
|
if mySQLError.Number == 1062 && strings.Contains(mySQLError.Message, "users_uc_email") {
|
|
return ErrDuplicateEmail
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *UserModel) Authenticate(email, password string) (int, error) {
|
|
var id int
|
|
var hashedPassword []byte
|
|
|
|
stmt := `SELECT id, hashed_password FROM users WHERE email = ?`
|
|
|
|
err := m.DB.QueryRow(stmt, email).Scan(&id, &hashedPassword)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return 0, ErrInvalidCredentials
|
|
}
|
|
return 0, err
|
|
}
|
|
err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
|
|
if err != nil {
|
|
if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
|
|
return 0, ErrInvalidCredentials
|
|
}
|
|
return 0, err
|
|
}
|
|
|
|
return id, nil
|
|
}
|
|
|
|
func (m *UserModel) Exists(id int) (bool, error) {
|
|
var exists bool
|
|
|
|
stmt := `SELECT EXISTS(SELECT true FROM users WHERE id = ?)`
|
|
|
|
err := m.DB.QueryRow(stmt, id).Scan(&exists)
|
|
return exists, err
|
|
}
|
|
|
|
func (a *UserModel) GetById(id int) (*User, error) {
|
|
acc := User{}
|
|
|
|
stmt := `SELECT name, email, created FROM users WHERE id = ?`
|
|
err := a.DB.QueryRow(stmt, id).Scan(&acc.Name, &acc.Email, &acc.Created)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, ErrInvalidCredentials
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return &acc, nil
|
|
}
|
|
|
|
func (a *UserModel) ChangePassword(id int, curr_pass, new_pass string) error {
|
|
hashedNewPassword, err := bcrypt.GenerateFromPassword([]byte(new_pass), 12)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var hashedDBPassword []byte
|
|
stmt := `SELECT hashed_password FROM users WHERE id = ?`
|
|
err = a.DB.QueryRow(stmt, id).Scan(&hashedDBPassword)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = bcrypt.CompareHashAndPassword(hashedDBPassword, []byte(curr_pass))
|
|
if err != nil {
|
|
if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
|
|
return ErrInvalidCredentials
|
|
}
|
|
return err
|
|
}
|
|
|
|
stmt = `UPDATE users SET hashed_password = ? WHERE id = ?`
|
|
_, err = a.DB.Exec(stmt, hashedNewPassword, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|