Проверка на пустой пароль в Rails

Я добавил проверку пароля в свою модель User:

validates :password, presence: true
validates :password, confirmation: { case_sensitive: true }

но затем, когда я хотел обновить другие поля в users, эти проверки делали транзакцию недействительной, поскольку password отсутствовал.

Проведя небольшое исследование, я понял, что могу пропустить эти проверки, если password отсутствует:

validates :password, presence: true, if: :validate_password?
validates :password, confirmation: { case_sensitive: true }, if: :validate_password?


def validate_password?
  password.present? || password_confirmation.present?
end

Однако теперь, когда я отправляю пустой пароль и подтверждение пароля, validate_password? возвращает false. Я не очень понимаю, что происходит, потому что

@user.update_attributes(password_reset_edit_params) возвращает true

где

password_reset_edit_params is <ActionController::Parameters {"password"=>"", "password_confirmation"=>""} permitted: true>

но внутри

def validate_password?
  password.present? || password_confirmation.present?
end

password и password_confirmation оцениваются как nil, а мой пароль @user не обновляется до пустой строки.

Я должен упомянуть, что я использую Bcrypt, и @user.password на самом деле всегда будет оцениваться как nil, тогда как доступно password_digest.

Итак, каково решение? В конечном счете, я думаю, мой вопрос просто:

Как можно игнорировать проверки пароля, когда я не пытаюсь отправить пароль, но также разрешать эти проверки при отправке пустой строки?

Мне пришло в голову, что я мог бы добавить условие в контроллер, которое не позволяет передавать пустые строки, но должен быть изящный способ решить эту проблему.

Любая помощь очень ценится. Спасибо!


person Josh Kestenberg    schedule 22.03.2019    source источник
comment
Удалите , if: :validate_password? из проверки.   -  person Aleksei Matiushkin    schedule 22.03.2019
comment
Но затем это возвращает меня к моей первой проблеме, когда каждый раз, когда я пытаюсь обновить экземпляр Userpassword или без него), запускается проверка, и если я пытаюсь обновить пользователя без ввода пароля, проверка возвращает ошибку.   -  person Josh Kestenberg    schedule 22.03.2019


Ответы (3)


Если вы используете Bcrypt и дайджест, вы можете использовать, например, в своей модели

has_secure_password
validates :password, length: { minimum: 8 }, allow_blank: true

В этом случае проверка будет работать только для установки и смены пароля.

Если вы не меняете пароль, вам не нужно вводить пароль.

По-другому

validates :password, presence: true, on: :create
person mechnicov    schedule 22.03.2019
comment
У меня возникли трудности с этим: если я включу allow_blank: true, а затем будет отправлен пустой пароль, например @user.update_attributes(password: '', password_confirmation: ''), ошибка не будет возвращена. Как я могу вернуть ошибку can't be blank, а также разрешить обновления без включения пароля? - person Josh Kestenberg; 22.03.2019
comment
allow_blank действительно решает большую часть моей проблемы. Мне просто нужно добавить ошибки в контроллер, если в качестве пароля будет отправлена ​​​​пустая строка. Спасибо. - person Josh Kestenberg; 22.03.2019
comment
Смотрите мой ответ ниже, чтобы понять, что я имею в виду. - person Josh Kestenberg; 22.03.2019
comment
Зачем тебе это? Проверка параметров и толстый контроллер — это не путь Rails - person mechnicov; 22.03.2019
comment
Это единственный способ, который я смог придумать, чтобы запретить пользователю вводить password: '' и password_confirmation: ''. Это крайний случай, который я не мог решить никаким другим способом. - person Josh Kestenberg; 22.03.2019
comment
allow_blank действителен для nil и пустой строки, в отличие от allow_nil. Вы можете использовать его - person mechnicov; 22.03.2019

В Rails вы не проверяете параметр, вы проверяете модель.

Не «пропускать проверку, если пароль отсутствует». Это цель проверки. Проверка предназначена для проверки модели, а не параметров. Если у модели пустой пароль, а наличие пароля требуется, это недопустимая модель. Всегда.

но затем, когда я хотел обновить другие поля для пользователей, эти проверки делали транзакцию недействительной, поскольку пароль отсутствовал.

Это не потому, что пароль отсутствует, а потому, что в экземпляре пользователя нет пароля. Как это должно работать:

  1. Пользователь создан с паролем и подтвержден: User.create(name: "Me", password: "something that isn't nil")
  2. Пользователь обновлен: User.update(name: "New name") # password is inside the model still so it is still valid

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

person Veridian Dynamics    schedule 22.03.2019
comment
Мой опыт противоречит вашему объяснению. Когда я удаляю validate_password? и пытаюсь сделать что-то вроде: @user.update_attributes(first_name: 'jimmy') запись не сохраняется, а @user.errors.full_messages оценивается как ["Password can't be blank"] - person Josh Kestenberg; 22.03.2019
comment
Есть ли у @user пароль? Покажи мне результат p @user.password - person Veridian Dynamics; 22.03.2019
comment
Это nil (как я объяснил в своем первом посте), но у пользователя есть password_digest, а @user.authenticate('password') возвращает желаемый результат. - person Josh Kestenberg; 22.03.2019
comment
Вам нужно удалить проверки спагетти и начать с самого простого validates_presence_of :password. Если пароль должен присутствовать, то это правильная проверка. Полная остановка. Если у вашего @user нет пароля, они не могут существовать. Просто как тот. Что-нибудь еще, начните новую тему по вашей новой проблеме. То, что вы только что сделали в комментариях, подтверждает мою точку зрения. Вы пытались обновить атрибут @user, у которого нет пароля, и проверка не удалась. Идеально. Мы хотим этого. Теперь дайте @user :password и сохраните их. Затем попробуйте обновить их. В этом-то и дело. - person Veridian Dynamics; 22.03.2019
comment
Но bcrypt не хранит пароль в модели User. Он принимает пароль и, необязательно, подтверждение пароля и создает хешированный пароль, называемый дайджестом пароля. Модель включает метод bcrpyt has_secure_password, который включает проверки при создании пользователя, но не касается обновления пользователя. api.rubyonrails.org/classes/ActiveModel/SecurePassword/ это просто неотъемлемая проблема bcrypt? Если я хочу обновить пароль пользователя, я должен отправить пароль и подтверждение пароля, но эти два поля не сохраняются в db. - person Josh Kestenberg; 22.03.2019
comment
Таким образом, в модели невозможно проверить пароль и подтверждение пароля, верно? Я думаю, мне нужно вместо этого убедиться, что входные данные формы приемлемы. - person Josh Kestenberg; 22.03.2019
comment
Верно, но именно поэтому вы не должны делать это так, как вы это делаете. Это не меняет того факта, что вы упускаете возможность проверки. Чтобы сэкономить время нам обоим: has_secure_password — это способ проверки пароля BCrypt. Вы можете проверить :password как validates :password, allow_nil: true, но если вы не хотите разрешать nil, игнорируйте это и просто позвольте BCrypt/Rails сделать всю работу за вас. - person Veridian Dynamics; 22.03.2019

В конечном итоге я просто проверяю форматирование пароля и подтверждение модели пользователя:

validates :password,  confirmation: true,
                      length: {within: 6..40},
                      allow_blank: true

и очистка входных данных от пустого пароля/подтверждения пароля в контроллере.

if password_reset_edit_params[:password].blank? || password_reset_edit_params[:password_confirmation].blank?
      @user.errors[:password] << "can't be blank" if password_reset_edit_params[:password].blank?
      @user.errors[:password_confirmation] << "can't be blank" if password_reset_edit_params[:password_confirmation].blank?
      render 'edit'
person Josh Kestenberg    schedule 22.03.2019