user_pass.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. package login
  2. import (
  3. "encoding/base64"
  4. "fmt"
  5. "time"
  6. "github.com/google/uuid"
  7. "golang.org/x/crypto/bcrypt"
  8. "gorm.io/gorm"
  9. )
  10. type UserPasser interface {
  11. FindUser(db *gorm.DB, model interface{}, account string) (user interface{}, err error)
  12. EncryptPassword()
  13. IsPasswordCorrect(password string) bool
  14. IncreaseRetryCount(db *gorm.DB, model interface{}) error
  15. GenerateResetPasswordToken(db *gorm.DB, model interface{}) (token string, err error)
  16. ConsumeResetPasswordToken(db *gorm.DB, model interface{}) error
  17. GetAccountName() string
  18. GetPasswordUpdatedAt() string
  19. GetLoginRetryCount() int
  20. GetLocked() bool
  21. GetIsTOTPSetup() bool
  22. GetTOTPSecret() string
  23. GetLastUsedTOTPCode() (code string, usedAt *time.Time)
  24. GetResetPasswordToken() (token string, createdAt *time.Time, expired bool)
  25. SetPassword(db *gorm.DB, model interface{}, password string) error
  26. SetIsTOTPSetup(db *gorm.DB, model interface{}, v bool) error
  27. SetTOTPSecret(db *gorm.DB, model interface{}, key string) error
  28. SetLastUsedTOTPCode(db *gorm.DB, model interface{}, passcode string) error
  29. LockUser(db *gorm.DB, model interface{}) error
  30. UnlockUser(db *gorm.DB, model interface{}) error
  31. }
  32. type SessionSecureUserPasser interface {
  33. SessionSecurer
  34. UserPasser
  35. }
  36. type UserPass struct {
  37. Account string `gorm:"index:uidx_users_account,unique,where:account!='' and deleted_at is null"`
  38. Password string `gorm:"size:60"`
  39. // UnixNano string
  40. PassUpdatedAt string
  41. LoginRetryCount int
  42. Locked bool
  43. LockedAt *time.Time
  44. ResetPasswordToken string `gorm:"index:uidx_users_reset_password_token,unique,where:reset_password_token!=''"`
  45. ResetPasswordTokenCreatedAt *time.Time
  46. ResetPasswordTokenExpiredAt *time.Time
  47. TOTPSecret string
  48. IsTOTPSetup bool
  49. LastUsedTOTPCode string
  50. LastTOTPCodeUsedAt *time.Time
  51. }
  52. var _ UserPasser = (*UserPass)(nil)
  53. func (up *UserPass) FindUser(db *gorm.DB, model interface{}, account string) (user interface{}, err error) {
  54. err = db.Where("account = ?", account).
  55. First(model).
  56. Error
  57. if err != nil {
  58. return nil, err
  59. }
  60. return model, nil
  61. }
  62. func (up *UserPass) GetAccountName() string {
  63. return up.Account
  64. }
  65. func (up *UserPass) GetLoginRetryCount() int {
  66. return up.LoginRetryCount
  67. }
  68. func (up *UserPass) GetLocked() bool {
  69. if !up.Locked {
  70. return false
  71. }
  72. return up.Locked && up.LockedAt != nil && time.Now().Sub(*up.LockedAt) <= time.Hour
  73. }
  74. func (up *UserPass) GetTOTPSecret() string {
  75. return up.TOTPSecret
  76. }
  77. func (up *UserPass) GetIsTOTPSetup() bool {
  78. return up.IsTOTPSetup
  79. }
  80. func (up *UserPass) EncryptPassword() {
  81. if up.Password == "" {
  82. return
  83. }
  84. hash, err := bcrypt.GenerateFromPassword([]byte(up.Password), 10)
  85. if err != nil {
  86. panic(err)
  87. }
  88. up.Password = string(hash)
  89. up.PassUpdatedAt = fmt.Sprint(time.Now().UnixNano())
  90. }
  91. func (up *UserPass) IsPasswordCorrect(password string) bool {
  92. return bcrypt.CompareHashAndPassword([]byte(up.Password), []byte(password)) == nil
  93. }
  94. func (up *UserPass) GetPasswordUpdatedAt() string {
  95. return up.PassUpdatedAt
  96. }
  97. func (up *UserPass) LockUser(db *gorm.DB, model interface{}) error {
  98. lockedAt := time.Now()
  99. if err := db.Model(model).Where("account = ?", up.Account).Updates(map[string]interface{}{
  100. "locked": true,
  101. "locked_at": &lockedAt,
  102. }).Error; err != nil {
  103. return err
  104. }
  105. up.Locked = true
  106. up.LockedAt = &lockedAt
  107. return nil
  108. }
  109. func (up *UserPass) UnlockUser(db *gorm.DB, model interface{}) error {
  110. if err := db.Model(model).Where("account = ?", up.Account).Updates(map[string]interface{}{
  111. "locked": false,
  112. "login_retry_count": 0,
  113. "locked_at": nil,
  114. }).Error; err != nil {
  115. return err
  116. }
  117. up.Locked = false
  118. up.LoginRetryCount = 0
  119. up.LockedAt = nil
  120. return nil
  121. }
  122. func (up *UserPass) IncreaseRetryCount(db *gorm.DB, model interface{}) error {
  123. if err := db.Model(model).Where("account = ?", up.Account).Updates(map[string]interface{}{
  124. "login_retry_count": gorm.Expr("coalesce(login_retry_count,0) + 1"),
  125. }).Error; err != nil {
  126. return err
  127. }
  128. up.LoginRetryCount++
  129. return nil
  130. }
  131. func (up *UserPass) GenerateResetPasswordToken(db *gorm.DB, model interface{}) (token string, err error) {
  132. token = base64.URLEncoding.EncodeToString([]byte(uuid.NewString()))
  133. now := time.Now()
  134. expiredAt := now.Add(10 * time.Minute)
  135. err = db.Model(model).
  136. Where("account = ?", up.Account).
  137. Updates(map[string]interface{}{
  138. "reset_password_token": token,
  139. "reset_password_token_created_at": now,
  140. "reset_password_token_expired_at": expiredAt,
  141. }).
  142. Error
  143. if err != nil {
  144. return "", err
  145. }
  146. up.ResetPasswordToken = token
  147. up.ResetPasswordTokenCreatedAt = &now
  148. up.ResetPasswordTokenExpiredAt = &expiredAt
  149. return token, nil
  150. }
  151. func (up *UserPass) ConsumeResetPasswordToken(db *gorm.DB, model interface{}) error {
  152. err := db.Model(model).
  153. Where("account = ?", up.Account).
  154. Updates(map[string]interface{}{
  155. "reset_password_token_expired_at": time.Now(),
  156. }).
  157. Error
  158. if err != nil {
  159. return err
  160. }
  161. return nil
  162. }
  163. func (up *UserPass) GetResetPasswordToken() (token string, createdAt *time.Time, expired bool) {
  164. if up.ResetPasswordTokenExpiredAt != nil && time.Now().Sub(*up.ResetPasswordTokenExpiredAt) > 0 {
  165. return "", nil, true
  166. }
  167. return up.ResetPasswordToken, up.ResetPasswordTokenCreatedAt, false
  168. }
  169. func (up *UserPass) SetPassword(db *gorm.DB, model interface{}, password string) error {
  170. up.Password = password
  171. up.EncryptPassword()
  172. err := db.Model(model).
  173. Where("account = ?", up.Account).
  174. Updates(map[string]interface{}{
  175. "password": up.Password,
  176. "pass_updated_at": up.PassUpdatedAt,
  177. }).
  178. Error
  179. if err != nil {
  180. return err
  181. }
  182. return nil
  183. }
  184. func (up *UserPass) SetTOTPSecret(db *gorm.DB, model interface{}, key string) error {
  185. if err := db.Model(model).Where("account = ?", up.Account).Updates(map[string]interface{}{
  186. "totp_secret": key,
  187. }).Error; err != nil {
  188. return err
  189. }
  190. up.TOTPSecret = key
  191. return nil
  192. }
  193. func (up *UserPass) SetIsTOTPSetup(db *gorm.DB, model interface{}, v bool) error {
  194. if err := db.Model(model).Where("account = ?", up.Account).Updates(map[string]interface{}{
  195. "is_totp_setup": v,
  196. }).Error; err != nil {
  197. return err
  198. }
  199. up.IsTOTPSetup = v
  200. return nil
  201. }
  202. func (up *UserPass) SetLastUsedTOTPCode(db *gorm.DB, model interface{}, passcode string) error {
  203. now := time.Now()
  204. if err := db.Model(model).Where("account = ?", up.Account).Updates(map[string]interface{}{
  205. "last_used_totp_code": passcode,
  206. "last_totp_code_used_at": &now,
  207. }).Error; err != nil {
  208. return err
  209. }
  210. up.LastUsedTOTPCode = passcode
  211. return nil
  212. }
  213. func (up *UserPass) GetLastUsedTOTPCode() (code string, usedAt *time.Time) {
  214. return up.LastUsedTOTPCode, up.LastTOTPCodeUsedAt
  215. }