【问题标题】:Updating current_user attribute to true from password update controller action从密码更新控制器操作将 current_user 属性更新为 true
【发布时间】:2016-09-25 10:25:02
【问题描述】:

My Rails 5 App 仅允许管理员或支持用户创建用户,创建用户时会生成密码并通过电子邮件发送给用户,在第一次用户登录时,应用程序会强制他们更改密码。

我的架构中有一个 password_updated 字段,我希望在更新密码时将其填充为真,但是我在这里碰壁,不确定它的编码器眼睛是否,我只是看不出我哪里出错了。

我的应用程序控制器:

  # Force User To Change Password On First Login
  def after_sign_in_path_for(resource)
    if current_user.password_updated == "false"
      edit_passwords_path
    else
      authenticated_root_path
    end
  end

我已经设置好了,如果用户试图跳过或跳过密码更改,他们将被重定向到密码更改。

我的密码控制器:

class PasswordsController < ApplicationController
  def edit
    @user = current_user
  end

  def update
    if current_user.update_with_password(user_params)
      current_user.password_updated = "true"
      flash[:notice] = 'Your Password Has Been Sucessfully Updated.'
      redirect_to authenticated_root_path
    else
      flash[:error] = 'Oh No! Something Went Wrong, Please Try Again.'
      render :edit
    end
  end

  private
    def user_params
      params.require(:user).permit(:password_updated, :current_password, :password, :password_confirmation)
    end

end

最初我让应用程序控制器查看登录计数,但是如果用户关闭并等待足够长的时间,他们可以重新登录而不必更改密码。我觉得这个风险更大。

这里的任何帮助将不胜感激。

【问题讨论】:

  • password_updated 在 DB 中是布尔值还是字符串?
  • @Aleksey 它是一个字符串,如有必要我可以轻松将其更改为布尔值
  • 这不是必需的,但我认为这会很好。只是我的建议 =)
  • 好吧我可以改变它,无论哪种方式仍然无法正常工作lol..
  • 我不知道它只是通过更改就可以工作)现在我正在检查您的代码。

标签: ruby-on-rails ruby-on-rails-4 devise


【解决方案1】:

我认为问题在于您在将password_updated 设置为true 后没有保存current_user
所以代码应该是这样的

def update
  update_params = user_params.merge(password_updated: true)
  if current_user.update_with_password(update_params)
    flash[:notice] = 'Your Password Has Been Successfully Updated.'
    redirect_to authenticated_root_path
  else
    flash[:error] = 'Oh No! Something Went Wrong, Please Try Again.'
    render :edit
  end
end

这样您只需保存一次current_user

我想password_updated 是 DB 中的布尔字段。
然后在您的application_controller.rb 中,您可以像current_user.password_updated? 一样检查它。

【讨论】:

    【解决方案2】:

    我建议允许管理员创建一个没有密码的用户。您必须从 devise 覆盖 password_required 方法。

    def password_required?
       new_record? ? false : true
    end
    

    对于新记录,管理员创建时不需要密码,但用户注册时会提示添加密码。 或者你甚至可以保持条件,比如当用户是管理员时,返回 false,否则返回 true。

    【讨论】:

    • 设置“真实”密码时不允许用户进行身份验证。
    【解决方案3】:

    我会选择使用邀请的不同方法。

    受邀用户是使用用于识别用户的令牌(密码随机字符串)创建的。这消除了以明文形式传达临时密码的需要,例如,您可以为邀请令牌添加到期时间以确保安全。

    所以应用流程如下:

    • 管理员访问/invitiations/new
    • 他用新用户的电子邮件填写表格并发送到/invitations
    • 会向新用户发送一封电子邮件,其中包含带有访问令牌的链接。
    • 新用户点击链接发送到/invitations/edit?invitation_token=ABCD12
    • 用户使用密码填写表单,然后将 PATCH 发送到/invitations,请求正文中包含邀请令牌。
    • 然后应提示用户使用新密码进行签名。

    一个最小的例子是:

    class AddInvitationTokenToUser < ActiveRecord::Migration[5.0]
      def change
        add_column :users, :invitation_token, :string
        # this may not work on DBs that do not allow NULL on unique columns
        add_index :users, :invitation_token, unique: true
      end
    end
    

    然后我们需要设置 User 模型来创建随机邀请令牌。

    require 'securerandom'
    class User < ApplicationRecord
    
      # @todo skip password validation if user has invitation_token
    
      def set_invitation_token!
        self.invitation_token = generate_invitation_token
      end
    
      private 
    
        def generate_invitation_token
          # this ensures that the token is unique
          begin
            token = SecureRandom.urlsafe_base64
          end while User.where(invitation_token: token).any?
          token
        end
    end
    

    为邀请设置控制器:

    class InvitationsController < ApplicationController
    
      before_action :authenticate!, only: [:new, :create]
      before_action :authorize!, only: [:new, :create]
    
      prepend_before_action :authenticate_user_from_token!, only: [:edit, :update]
      skip_before_action :authenticate!, :authorize!, only: [:edit, :update]
    
      def new
        @user = User.new
      end
    
      def create
        @user = User.new(create_params) do |u|
          u.set_invitation_token!
        end
        if @user.save
          # @todo email user invitation email
          redirect_to '/somewhere'
        else
          render :new
        end
      end
    
      def edit
      end
    
      def update
        if @user.update(update_params)
          @user.update_attibute(:invitation_token, nil)
          redirect_to new_session_path, notice: 'Please sign in.'
        else
          render :edit
        end
      end
    
      private
    
        def authenticate_user_from_token!
          unless params[:invitation_token].present?
            raise ActiveRecord::RecordNotFound and return 
          end 
          @user = User.find_by!(invitation_token: params[:invitation_token])
        end
    
        def create_params
          require(:user).permit(:email)
        end
    
        def update_params
          require(:user).permit(:password, :password_confirmation)
        end
    end
    

    为简洁起见,此处省略了几个步骤,例如跳过密码验证。

    我建议您查看 DeviseInvitable 以获取有关此模式的更完整示例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-20
      • 1970-01-01
      • 2016-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-28
      • 2021-05-30
      相关资源
      最近更新 更多