GoScrobble/internal/goscrobble/user.go

107 lines
2.5 KiB
Go

package goscrobble
import (
"errors"
"fmt"
"time"
"golang.org/x/crypto/bcrypt"
)
const bCryptCost = 16
type User struct {
UUID string `json:"uuid"`
CreatedAt time.Time `json:"created_at"`
Username string `json:"username"`
password []byte
Email string `json:"email"`
Verified bool `json:"verified"`
Active bool `json:"active"`
Admin bool `json:"admin"`
}
// RegisterRequest - Incoming JSON
type RegisterRequest struct {
Username string `json:"username"`
Email string `json:"email"`
Password string `json:"password"`
}
// createUser - Called from API
func createUser(req *RegisterRequest) error {
// Check if user already exists..
if len(req.Password) < 8 {
return errors.New("Password must be at least 8 characters")
}
// Check username is set
if req.Username == "" {
return errors.New("A username is required")
}
// If set an email.. validate it!
if req.Email != "" {
if !isEmailValid(req.Email) {
return errors.New("Invalid email address")
}
}
// Check if user or email exists!
if userAlreadyExists(req) {
return errors.New("Username or email already exists")
}
// Lets hashit!
hash, err := hashPassword(req.Password)
if err != nil {
return err
}
return insertUser(req.Username, req.Email, hash)
}
// insertUser - Does the dirtywork!
func insertUser(username string, email string, password []byte) error {
_, err := db.Exec("INSERT INTO users (uuid, created_at, username, email, password) VALUES (UUID_TO_BIN(UUID(), true),NOW(),?,?,?)", username, email, password)
return err
}
// hashPassword - Returns bcrypt hash
func hashPassword(password string) ([]byte, error) {
return bcrypt.GenerateFromPassword([]byte(password), bCryptCost)
}
// isValidPassword - Checks if password is valid
func isValidPassword(password string, user User) bool {
err := bcrypt.CompareHashAndPassword(user.password, []byte(password))
if err != nil {
return false
}
return true
}
// userAlreadyExists - Returns bool indicating if a record exists for either username or email
// Using two look ups to make use of DB indexes.
func userAlreadyExists(req *RegisterRequest) bool {
var userExists int
err := db.QueryRow("SELECT COUNT(*) FROM users WHERE username = ?", req.Username).Scan(&userExists)
if userExists > 0 {
return true
}
if req.Email != "" {
// Only run email check if there's an email...
err = db.QueryRow("SELECT COUNT(*) FROM users WHERE email = ?", req.Email).Scan(&userExists)
}
if err != nil {
fmt.Printf("Error querying for duplicate users: %v", err)
return true
}
return userExists > 0
}