attr_accessor и проверка пароля при обновлении - PullRequest
5 голосов
/ 17 ноября 2011

У меня есть этот код в моей модели пользователя:

class User < ActiveRecord::Base
  attr_accessible :email, :password, :password_confirmation
  attr_accessor :password

  before_save :encrypt_password

  validates :email, :presence => true,
                    :uniqueness => { :case_sensitive => false },
                    :format => { :with => /\A[^@]+@[^@]+\z/ },
                    :length => 7..128
  validates :password, :presence => true,
                       :confirmation => true,
                       :length => 6..128

  private
    def encrypt_password
      return unless password
      self.encrypted_password = BCrypt::Password.create(password)
    end
end

Теперь в моем контроллере, когда я обновляю некоторые пользовательские поля с помощью

@user.update_attributes(params[:user])

поле пароля всегда проверяется, даже если оно не задано в хэше params. Я понял, что это происходит из-за attr_accesor: пароля, который всегда устанавливает пароль = "" в update_attributes.

Теперь я могу просто пропустить проверку пароля, если это пустая строка:

validates :password, :presence => true,
                     :confirmation => true,
                     :length => 6..128,
                     :if => "password.present?"

Но это не работает, потому что позволяет пользователю установить пустой пароль.

Использование update_attribute в поле, которое я хотел бы изменить, не является решением, поскольку мне требуется проверка этого атрибута. Если я передам точный параметр с

@user.update_attributes(params[:user][:fieldname])

это не решает проблему, поскольку также запускает проверку пароля.

Есть ли способ предотвратить attr_accesor: пароль всегда устанавливает пароль = "" при обновлении?

Ответы [ 2 ]

14 голосов
/ 17 ноября 2011

Новый ответ

Это работает для меня:

validates :password, :presence     => true,
                     :confirmation => true,
                     :length       => { :minimum => 6 },
                     :if           => :password # only validate if password changed!

Если я правильно помню, мне потребовалось некоторое время, чтобы понять это правильно (много проби ошибка).У меня никогда не было времени, чтобы точно выяснить, почему это работает (в отличие от :if => "password.present?").

Старый ответ - не очень полезен для ваших целей (см. Комментарии) Я обхожу этопроблема с использованием совершенно другого действия для обновления пароля (user # update_password).Теперь достаточно только проверить поле пароля

:on => [:create, :update_password]

(а также сделать его доступным только для этих действий).

Вот еще несколько подробностей:

в вашеммаршруты:

resources :users do
  member do
    GET :edit_password # for the user#edit_password action
    PUT :update_password # for the user#update_passwor action
  end
end

в вашем UsersController:

def edit_password
  # could be same content as #edit action, e.g.
  @user = User.find(params[:id])
end
def update_password
  # code to update password (and only password) here
end

В вашем представлении edit_password теперь у вас есть форма только для обновления пароля, очень похожая на вашу форму в представлении редактирования,но с: method =>: put и: url => edit_password_user_path (@user)

2 голосов
/ 17 ноября 2011

Решение, которое я начал использовать для решения этой проблемы, заключается в следующем:

Начните использовать встроенный метод ActiveModel has_secure_password.

На консоли

rails g migration add_password_digest_to_users password_digest:string
rake db:migrate

В вашей модели:

class User < ActiveRecord::Base
  has_secure_password

  attr_accessible :login_name, :password, :password_confirmation

  # secure_password.rb already checks for presence of :password_digest
  # so we can assume that a password is present if that validation passes
  # and thus, we don't need to explicitly check for presence of password
  validates :password, 
    :length => { :minimum => 6 }, :if => :password_digest_changed?

  # secure_password.rb also checks for confirmation of :password 
  # but we also have to check for presence of :password_confirmation
  validates :password_confirmation, 
    :presence=>true, :if => :password_digest_changed?
end

И, наконец,

# In `config/locales/en.yml` make sure that errors on
# the password_digest field refer to "Password" as it's more human friendly 

en:
  hello: "Hello world"

  activerecord:
    attributes:
      user:
        password_digest: "Password"  

О, еще одна вещь: смотрите railscast

...