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

  1. package models
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. "git.kirsle.net/apps/gophertype/pkg/console"
  7. "git.kirsle.net/apps/gophertype/pkg/constants"
  8. "golang.org/x/crypto/bcrypt"
  9. )
  10. type userMan struct{}
  11. // Users is a singleton helper to deal with user models.
  12. var Users = userMan{}
  13. // User account for the site.
  14. type User struct {
  15. BaseModel
  16. Email string `gorm:"unique_index"`
  17. Name string
  18. HashedPassword string `json:"-"`
  19. IsAdmin bool `gorm:"index"`
  20. // Relationships
  21. Posts []Post `gorm:"foreignkey:AuthorID"`
  22. }
  23. // Validate the User object has everything filled in. Fixes what it can,
  24. // returns an error if something is wrong. Ensures the HashedPassword is hashed.
  25. func (u *User) Validate() error {
  26. u.Email = strings.TrimSpace(strings.ToLower(u.Email))
  27. u.Name = strings.TrimSpace(u.Name)
  28. if len(u.Email) == 0 {
  29. return errors.New("Email is required")
  30. }
  31. return nil
  32. }
  33. // AuthenticateUser checks a login for an email and password.
  34. func (m userMan) AuthenticateUser(email string, password string) (User, error) {
  35. user, err := m.GetUserByEmail(email)
  36. if err != nil {
  37. console.Error("AuthenticateUser: email %s not found: %s", email, err)
  38. return User{}, errors.New("incorrect email or password")
  39. }
  40. if user.VerifyPassword(password) {
  41. return user, nil
  42. }
  43. return User{}, errors.New("incorrect email or password")
  44. }
  45. // GetUserByID looks up a user by their ID.
  46. func (m userMan) GetUserByID(id int) (User, error) {
  47. var user User
  48. r := DB.First(&user, id)
  49. return user, r.Error
  50. }
  51. // GetUserByEmail looks up a user by their email address.
  52. func (m userMan) GetUserByEmail(email string) (User, error) {
  53. var user User
  54. r := DB.Where("email = ?", strings.ToLower(email)).First(&user)
  55. return user, r.Error
  56. }
  57. // ListAdminEmails returns the array of email addresses of all admin users.
  58. func (m userMan) ListAdminEmails() ([]string, error) {
  59. var (
  60. users []User
  61. emails []string
  62. )
  63. r := DB.Where("is_admin=true AND email IS NOT NULL").Find(&users)
  64. for _, user := range users {
  65. if len(user.Email) > 0 {
  66. emails = append(emails, user.Email)
  67. }
  68. }
  69. return emails, r.Error
  70. }
  71. // SetPassword stores the hashed password for a user.
  72. func (u *User) SetPassword(password string) error {
  73. hash, err := bcrypt.GenerateFromPassword([]byte(password), constants.BcryptCost)
  74. if err != nil {
  75. return fmt.Errorf("SetPassword: %s", err)
  76. }
  77. u.HashedPassword = string(hash)
  78. return nil
  79. }
  80. // VerifyPassword checks if the password matches the user's hashed password.
  81. func (u *User) VerifyPassword(password string) bool {
  82. if u.HashedPassword == "" {
  83. console.Error("ERROR: VerifyPassword: user %s has no HashedPassword", u.Email)
  84. return false
  85. }
  86. err := bcrypt.CompareHashAndPassword([]byte(u.HashedPassword), []byte(password))
  87. if err == nil {
  88. return true
  89. }
  90. console.Error("VerifyPassword: %s", err)
  91. return false
  92. }
  93. // FirstAdmin returns the admin user with the lowest ID number.
  94. func (m userMan) FirstAdmin() (User, error) {
  95. var user User
  96. r := DB.First(&user, "is_admin = ?", true)
  97. return user, r.Error
  98. }
  99. // CreateUser adds a new user to the database.
  100. func (m userMan) CreateUser(u User) error {
  101. if err := u.Validate(); err != nil {
  102. return err
  103. }
  104. r := DB.Create(&u)
  105. return r.Error
  106. }