Ruby on Rails / Devise - Запрос пароля при смене электронной почты - PullRequest
8 голосов
/ 24 марта 2011

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

В любом случае, я использую Ruby on Rails 3 с Devise для аутентификации пользователей. Как вы, возможно, знаете, в пользовательском admin / edit по умолчанию пользователь должен ввести свой текущий пароль в поле current_password, если он предоставит новый пароль. Существует масса информации о том, как отключить current_password, чтобы пользователи могли свободно изменять и сохранять.

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

Просмотрев вики Devise, я нашел эту страницу и подумал, что смогу перевернуть этот код, чтобы завершить это решение. Мне удалось немного поработать в моей модели user.rb (я избавился от ненужной логики для этого поста) ....

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :confirmable, :lockable and :timeoutable
  devise :database_authenticatable, :registerable, :confirmable,
         :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :name, :email, :password, :password_confirmation, :avatar, :remember_me, :agree
  attr_accessor :accessible, :agree, :signed_in
  attr_readonly :name

  # Validation
  validates :name, :presence => TRUE, :uniqueness => TRUE, :length => { :within => 4..20 }
  validates :agree, :term_agreement => TRUE, :unless => :signed_in
  validates_attachment_size :avatar, :less_than => 1.megabyte
  validates_attachment_content_type :avatar, :content_type => ['image/jpeg', 'image/png', 'image/gif']
  validates :current_password, :presence => TRUE, :if => :password_required?

  protected

  def password_required?
    email_changed?
  end

end

Это "почти" работает. Если я сохраняю профиль пользователя, не меняя ничего, или меняю другое обязательное поле без пароля (например, аватар пользователя), профиль сохраняется нормально, пароль не требуется. Пока все хорошо.

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

В основном, ТОЛЬКО current_password должен быть обязателен, если адрес электронной почты изменен. Как мне заставить это работать в моем коде?

  • ОБНОВЛЕНИЕ *******

Я проверил свой логин в ответ на приведенное ниже предложение от bowsersenior и вижу следующие строки, когда я пытаюсь сохранить и обновить электронную почту ...

User Load (0.2ms)  SELECT `users`.`id` FROM `users` WHERE (LOWER(`users`.`email`) = LOWER('newaddress@changed.com')) AND (`users`.id <> 1) LIMIT 1
  User Load (0.2ms)  SELECT `users`.`id` FROM `users` WHERE (`users`.`name` = BINARY 'Administrator') AND (`users`.id <> 1) LIMIT 1
  SQL (0.1ms)  ROLLBACK

Интересно, имеет ли это «ROLLBACK» какое-то отношение к финальной проблеме?

Ответы [ 3 ]

5 голосов
/ 24 марта 2011

Попробуйте:

validates :current_password, :presence => TRUE, :if => :email_changed?

Я настоятельно рекомендую вам оставить password_required? в покое. Это может привести к ошибкам с безопасностью и нестабильным поведением.

4 голосов
/ 06 мая 2013

Я считаю, что способ разработки Devise заключается в следующем:

class RegistrationsController < Devise::RegistrationsController
  def update
    @user = User.find(current_user.id)

    successfully_updated = if needs_password?(@user, params)
      @user.update_with_password(params[:user])
    else
      # remove the virtual current_password attribute update_without_password
      # doesn't know how to ignore it
      params[:user].delete(:current_password)
      @user.update_without_password(params[:user])
    end

    if successfully_updated
      set_flash_message :notice, :updated
      redirect_to after_update_path_for(@user)
    else
      render "edit"
    end
  end

  private

  def needs_password?(user, params)
    user.email != params[:user][:email]
  end
end

Другими словами, все происходит на уровне контроллера, и мы используем User#update_with_password, когда пользователь хочет изменить свою электронную почту (мы знаем, что, вызывая needs_password? в действии обновления) или User#update_without_password, когда пользователь изменяет данные своего другого профиля.

Также помните, что вам необходимо "зарегистрировать" этот новый RegistrationsController в ваших маршрутах:

devise_for :users, :controllers => { :registrations => "registrations" }

Источник:

1 голос
/ 17 августа 2015

Аналогично ответу Павла, но вместо того, чтобы отменять обновление метода контроллера, как об этом, основываясь на devise wiki

class RegistrationsController < Devise::RegistrationsController
  protected

  def update_resource(resource, params)
    if resource.email != params[:email] || params[:password].present?
      super
    else
      params.delete(:current_password)
      resource.update_without_password(params)
    end
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...