A web blog and personal homepage engine written in Go.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

129 lines
3.2 KiB

package models
import (
"errors"
"fmt"
"strings"
"git.kirsle.net/apps/gophertype/pkg/console"
"git.kirsle.net/apps/gophertype/pkg/constants"
"golang.org/x/crypto/bcrypt"
)
type userMan struct{}
// Users is a singleton helper to deal with user models.
var Users = userMan{}
// User account for the site.
type User struct {
BaseModel
Email string `gorm:"unique_index"`
Name string
HashedPassword string `json:"-"`
IsAdmin bool `gorm:"index"`
// Relationships
Posts []Post `gorm:"foreignkey:AuthorID"`
}
// Validate the User object has everything filled in. Fixes what it can,
// returns an error if something is wrong. Ensures the HashedPassword is hashed.
func (u *User) Validate() error {
u.Email = strings.TrimSpace(strings.ToLower(u.Email))
u.Name = strings.TrimSpace(u.Name)
if len(u.Email) == 0 {
return errors.New("Email is required")
}
return nil
}
// AuthenticateUser checks a login for an email and password.
func (m userMan) AuthenticateUser(email string, password string) (User, error) {
user, err := m.GetUserByEmail(email)
if err != nil {
console.Error("AuthenticateUser: email %s not found: %s", email, err)
return User{}, errors.New("incorrect email or password")
}
if user.VerifyPassword(password) {
return user, nil
}
return User{}, errors.New("incorrect email or password")
}
// GetUserByID looks up a user by their ID.
func (m userMan) GetUserByID(id int) (User, error) {
var user User
r := DB.First(&user, id)
return user, r.Error
}
// GetUserByEmail looks up a user by their email address.
func (m userMan) GetUserByEmail(email string) (User, error) {
var user User
r := DB.Where("email = ?", strings.ToLower(email)).First(&user)
return user, r.Error
}
// ListAdminEmails returns the array of email addresses of all admin users.
func (m userMan) ListAdminEmails() ([]string, error) {
var (
users []User
emails []string
)
r := DB.Where("is_admin=true AND email IS NOT NULL").Find(&users)
for _, user := range users {
if len(user.Email) > 0 {
emails = append(emails, user.Email)
}
}
return emails, r.Error
}
// SetPassword stores the hashed password for a user.
func (u *User) SetPassword(password string) error {
hash, err := bcrypt.GenerateFromPassword([]byte(password), constants.BcryptCost)
if err != nil {
return fmt.Errorf("SetPassword: %s", err)
}
u.HashedPassword = string(hash)
return nil
}
// VerifyPassword checks if the password matches the user's hashed password.
func (u *User) VerifyPassword(password string) bool {
if u.HashedPassword == "" {
console.Error("ERROR: VerifyPassword: user %s has no HashedPassword", u.Email)
return false
}
err := bcrypt.CompareHashAndPassword([]byte(u.HashedPassword), []byte(password))
if err == nil {
return true
}
console.Error("VerifyPassword: %s", err)
return false
}
// FirstAdmin returns the admin user with the lowest ID number.
func (m userMan) FirstAdmin() (User, error) {
var user User
r := DB.First(&user, "is_admin = ?", true)
return user, r.Error
}
// CreateUser adds a new user to the database.
func (m userMan) CreateUser(u User) error {
if err := u.Validate(); err != nil {
return err
}
r := DB.Create(&u)
return r.Error
}