Ruby on Rails / Devise - 更改电子邮件时需要密码

发布于 2024-10-25 20:28:53 字数 2538 浏览 1 评论 0原文

就是这样,对于所有的弹珠,如果我能解决这个问题,那么我就完成了一个项目。

无论如何,我使用 Ruby on Rails 3 和 Devise 进行用户身份验证。您可能知道,默认情况下,在用户 admin/edit 中,如果用户提供新密码,则必须在 current_password 字段中输入当前密码。有大量关于如何禁用 current_password 的信息,以便用户可以自由更改和保存。

然而,我几乎找不到做相反的事情:需要更多字段的当前密码......在我的例子中,是电子邮件字段。并且仅在该电子邮件地址更改时才需要当前密码,如果密码保持不变则不需要。目前,用户可以自由更改电子邮件而无需提供当前密码,出于安全原因,我不希望这样做。

在浏览了 Devise wiki 后,我确实发现 此页面,我想我可以反转此代码来完成此解决方案。我设法在我的 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 和密码(对于新密码)字段都会根据需要触发。如果我只是为了它的地狱而填写所有三个密码(密码,密码确认,当前密码),它不会采取,只会再次给出验证错误。

基本上,如果电子邮件地址发生更改,则只需要 current_password。我如何在我的代码中实现此功能?

  • 更新 ********

我检查了我的日志以响应 Bowsersenior 的以下建议,当我尝试保存和更新电子邮件时看到以下几行...

User Load (0.2ms)  SELECT `users`.`id` FROM `users` WHERE (LOWER(`users`.`email`) = LOWER('[email protected]')) 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”是否与此有关最后一个问题?

This is it, for all the marbles, if I can get this issue solved then I have a project completed.

Anyway, I am using Ruby on Rails 3 with Devise for user authentication. As you may know, in the user admin/edit by default, a user has to enter their current password in the current_password field if they provide a new password. There is a TON of information out there on how to disable current_password so users can change and save freely.

However, I can find very little on doing the opposite: requiring the current password for more fields...in my case, the email field. AND only require the current password when that email addy is changed, not if it remains the same. Currently users can freely change their email without giving their current password, and for security reasons, I don't want this.

After looking through the Devise wiki, I did find this page and I thought I could reverse this code to complete this solution. I managed to work this little bit out in my user.rb model (I stripped out of the unnecessary logic for this post)....

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

It "almost" works. If I save the user profile with changing nothing, or change other non-password required field (like the user avatar), the profile saves fine, no password required. So far, so good.

And if I change the email address, the validation is triggered....but what happens is that both the current_password AND password (for new password) fields trigger as required. And if I fill in the password in all three (password, password_confirmation, current_password) just for the hell of it, it won't take, just gives a validation error again.

Basically, ONLY the current_password should be required if the email address is changed. How would I make this work in my code?

  • UPDATE *******

I checked out my log in response to the below suggestion by bowsersenior, and see the following lines when I attempt to save and update the email...

User Load (0.2ms)  SELECT `users`.`id` FROM `users` WHERE (LOWER(`users`.`email`) = LOWER('[email protected]')) 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

I wonder if that 'ROLLBACK' has something to do with the final issue?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

山田美奈子 2024-11-01 20:28:53

尝试一下:

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

我强烈建议您保留 password_required?。这可能会导致安全错误和不稳定的行为。

Give this a try:

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

I strongly suggest you leave password_required? alone. That could lead to bugs with security and unstable behavior.

梦回梦里 2024-11-01 20:28:53

我相信 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" }

来源:

I believe the Devise way of doing this is as follows:

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

In other words, everything happens at the Controller level and we use either User#update_with_password, when user want to change his email (we know that by invoking needs_password? in the update action) or User#update_without_password, when user changes his other profile data.

Also remember you need to "register" this new RegistrationsController in your routes:

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

Source:

昔梦 2024-11-01 20:28:53

与 Pawel 的答案类似,但不是覆盖控制器方法更新,基于 设计维基

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

Similar to Pawel's answer, but rather than override the controller method update, how about this, based on the 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文