2014-06-08 40 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表單之前,不會觸摸密碼。 –