2014-06-08 36 views
0

为用户使用密码重置机制。密码长度验证触发,我试图理解为什么。密码验证触发何时不应触发?

user.rb

class User < ActiveRecord::Base 

    has_secure_password 

    validates :password, length: { minimum: 6 } 
    ... 

    def create_password_reset_token 
    self.update_attributes!(password_reset_token: SecureRandom.urlsafe_base64, password_reset_sent_at: Time.zone.now) 
    end 

    def reset_password(params) 
    self.update_attributes!(params) 
    self.update_attributes!(password_reset_token: nil, password_reset_sent_at: nil) 
    end 

end 

password_resets_controller.rb

def create 
    user = User.find_by_email(params[:email]) 
    if user 
    user.create_password_reset_token 
    UserMailer.password_reset_email(user).deliver 
    redirect_to root_url, :notice => "Email sent with password reset instructions!" 
    else 
    flash[:error] = "A user with that email address could not be found." 
    render 'new' 
    end 
end 

def edit 
    @user = User.find_by_password_reset_token(params[:id]) 
    if @user 
    render 'edit' 
    else 
    flash[:error] = "Invalid password reset code." 
    redirect_to root_url 
    end 
end 

def update 
    @user = User.find_by_password_reset_token(params[:id]) 
    if @user.password_reset_sent_at < 2.hours.ago 
    flash[:error] = "Password reset has expired." 
    redirect_to new_password_reset_path 
    elsif @user.reset_password(user_params) 
    flash[:success] = "Password has been reset." 
    redirect_to root_url 
    else 
    render 'edit' 
    end 
end 

password_resets/new.html.erb

<%= form_tag password_resets_path, :method => :post do %> 
    <%= label_tag :email %> 
    <%= text_field_tag :email, params[:email] %> 

    <%= submit_tag "Reset Password" %> 
<% end %> 

password_resets/edit.html.erb:

<%= form_for @user, :url => password_reset_path(params[:id]) do |f| %> 

    <h1 class="centertext">Reset Password</h1> 

    <%= render 'shared/error_messages', object: f.object %> 

    <%= f.label :password %> 
    <%= f.password_field :password %> 

    <%= f.label :password_confirmation, "Confirm password" %> 
    <%= f.password_field :password_confirmation %> 

    <%= f.submit "Update password" %> 

<% end %> 

错误是:

Validation failed: Password is too short (minimum is 6 characters) 

抛出它是create_password_reset_token方法内的行:

self.update_attributes!(password_reset_token: SecureRandom.urlsafe_base64, password_reset_sent_at: Time.zone.now) 

为什么在验证触发在这里?我没有对密码本身做任何事情。我只是在用户记录中创建一个令牌和一段时间。

将验证更改为说on: :create使其不触发。问题是用户可以将密码重置为少于六个字符。

澄清

需要明确的是,操作顺序是:“我忘记了密码”

  1. 用户点击一个链接说
  2. 它们被带到password_reset_controller/new.html.erb。此表格有一个字段:电子邮件地址。他们输入他们的电子邮件并提交。
  3. 控制器会检查该用户是否存在。如果有,它会告诉模型生成一个password_reset_token。
  4. 控制器然后命令一个电子邮件发送给包含令牌的URL的用户。
  5. 用户单击URL。如果令牌有效,它们将被带到edit.html.erb并且他们输入他们的新电子邮件和它的确认。
  6. 控制器调用reset_password方法,该方法实际上会重置用户的密码。

当前,在步骤2中,验证触发后,他们进入他们的电子邮件,然后单击提交。

+0

为什么你的'new.html.erb'没有'password field'?如何在没有密码字段的情况下重置密码? “验证”在“密码字段”上被调用。所以验证是触发的,因为没有“密码字段”。 – Pavan

+0

我对密码重置控制器和相应的edit.html.erb有一个编辑方法。我省略了它们,因为验证在它到达之前就被触发了。 –

+0

我添加了它们以最大限度地减少混淆。 –

回答

0

我加入一个PROC语句的密码来解决这个验证,如下所示:

validates :password, length: { minimum: 6 }, unless: Proc.new { |a| !a.password_reset_token.nil? } 

现在验证在用户创建和密码重置期间运行,但在有密码重置令牌集的时间间隔期间运行。所有的测试都通过了。

0

您create_password_reset_token呼吁update_attributes方法,这将触发各个领域验证您的用户模型,并由此引发了密码验证,因为它不具有当前设置

你需要或者

1 )对这些特定字段使用update_attribute并且不会触发验证

2)在您的模型中添加一些password_reset字段或枚举值,并在单击密码重置按钮时将其设置为true,然后在您的系统中执行类似操作用户型号

has_secure_password :validations => false 
validates :password, length: {minimum: 6}, unless: -> { user_password_reset? } 

3)用色器件宝石照顾这对你

更新:

试试这个

def create_password_reset_token 
    self.update_attribute(:password_reset_token, SecureRandom.urlsafe_base64)   
    self.update_attribute(:password_reset_sent_at, Time.zone.now) 
end 
+0

我明白为什么验证会触发。我不明白的是为什么它失败了。用户当前拥有大于6个字符的密码。他们点击一个链接重置它并输入他们的电子邮件地址(这样系统可以向他们发送一封包含重置令牌的电子邮件)。用户从未有过少于六个字符的密码。 –

+0

因此,当他们点击按钮重置它时,密码在此时不会重置为空? – flylib

+0

这是正确的。在用户填写edit.html.erb表单之前,不会触摸密码。 –