sample_rails_tailwind/app/models/user.rb
songtianlun 32ec61fe00 feat: add password reset functionality
- Implement password reset request and form
- Add user validation and expiration checks
- Create integration tests for password reset process

This commit introduces a complete password reset feature, allowing
users to reset their passwords securely. It includes necessary
validations to ensure the user is valid and the reset token has
not expired. Additionally, integration tests have been added to
verify the functionality and edge cases, enhancing overall
application security and user experience.
2025-01-08 11:44:42 +08:00

96 lines
2.5 KiB
Ruby

class User < ApplicationRecord
attr_accessor :remember_token, :activation_token, :reset_token
# before_save { self.email = email.downcase }
# before_save { email.downcase! }
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: true
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
def User.new_token
SecureRandom.urlsafe_base64
end
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
remember_digest
end
# 返回一个会话令牌,防止会话劫持
# 简单起见,直接使用记忆令牌
def session_token
remember_digest || remember
end
class << self
def digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
def new_token
SecureRandom.urlsafe_base64
end
end
# powered by metaprogramming
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
def forget
update_attribute(:remember_digest, nil)
end
def activate
# update_attribute(:activated, true)
# update_attribute(:activated_at, Time.zone.now)
update_columns(activated: true, activated_at: Time.zone.now)
end
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
def create_reset_digest
self.reset_token = User.new_token
update_columns(reset_digest: User.digest(reset_token),
reset_send_at: Time.zone.now)
end
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
def password_reset_expired?
reset_send_at < 2.hours.ago
end
private
def downcase_email
# self.email = email.downcase
email.downcase!
end
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end