簡體   English   中英

設計無密碼更新用戶

[英]Devise update user without password

我想在沒有密碼的情況下更新用戶屬性。 情況就像,如果密碼和密碼確認字段不為空,那么我需要設計錯誤,如果它們為空,則需要更新其他用戶屬性。 我怎么能用設計做到這一點?

提前致謝!

我認為這是一個更好的解決方案:

if params[:user][:password].blank? && params[:user][:password_confirmation].blank?
    params[:user].delete(:password)
    params[:user].delete(:password_confirmation)
end

這可以防止您通過簡單地從表單響應中刪除密碼字段(如果它是空白的)來更改設計控制器。

請務必@user.attributes = params[:user]或您在update操作中使用的任何內容之前使用它來設置表單中的新參數。

這是你要找的嗎? 來自設計維基

允許用戶在不提供密碼的情況下編輯他們的帳戶

它顯示了如何為 rails 3 和 rails 4 執行此操作

========================

約翰的解決方案是一個更簡單的選擇

我認為使用 Devise 3.2+ 你可以在你的子類控制器中覆蓋 update_resource 。 這是最初提出的問題的示例:

class MyRegistrationsController < Devise::RegistrationsController
  protected

  def update_resource(resource, params)
    resource.update_without_password(params)
  end
end

我解決這個問題如下:如果表單提交密碼,則需要填寫 current_password 字段或沒有密碼和 current_password 更新表單

在模型中:

class User
  devise: bla-bla-bla

  attr_accessor :current_password
end

在控制器中:

class Users::RegistrationsController <  Devise::RegistrationsController
  layout 'application'


   def update
    self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)


    # custom logic
    if params[:user][:password].present?
      result = resource.update_with_password(params[resource_name])
    else
      result = resource.update_without_password(params[resource_name])
    end

    # standart devise behaviour
    if result
      if is_navigational_format?
        if resource.respond_to?(:pending_reconfirmation?) && resource.pending_reconfirmation?
          flash_key = :update_needs_confirmation
        end
        set_flash_message :notice, flash_key || :updated
      end
      sign_in resource_name, resource, :bypass => true
      respond_with resource, :location => after_update_path_for(resource)
    else
      clean_up_passwords resource
      respond_with resource
    end
  end
end

如果您仍然希望支持密碼更改但使其可選,請檢查current_password的可用性,如下所示:

class MyRegistrationsController < Devise::RegistrationsController
  protected

  def update_resource(resource, params)
    if params[:current_password].blank?
     resource.update_without_password(params.except(:current_password))
    else
      resource.update_with_password(params)
    end
  end
end

這樣,如果 current_password 存在,您可以繼續並更新密碼,否則忽略它並在沒有密碼的情況下更新。

params[:user][:password] 在 rails 6 中不起作用

您必須將params[:user][:password]更改為params[:password]

刪除所有參數中的 [:user]

我的源代碼在下面

在運行此命令之前

rails generate devise:controllers users -c=registrations

class Users::RegistrationsController < Devise::RegistrationsController
  # before_action :configure_sign_up_params, only: [:create]
  # before_action :configure_account_update_params, only: [:update]

  protected

  def update_resource(resource, params)
    puts "params ===> #{params}"
    if params[:password].blank? && params[:password_confirmation].blank?
      params.delete(:password)
      params.delete(:password_confirmation)
      params.delete(:current_password)
      resource.update_without_password(params)
    else
      super
    end
  end
end

我用我的界面解決了這個問題。 它有兩種方式。

如果字段為空,則禁用它們

如果字段為空,jQuery 的這一位會在表單提交之前禁用這些字段。 它需要一定的標記模式,請查看Codepen 上演示

$(".password-fields").each(function() {
  var $fields, $form;
  $form = $(this).parents("form");
  $fields = $(this).find("input");
  $form.on("submit", function() {
    $fields.each(function() {
      if ($(this).val() === "") {
        $(this).attr("disabled", "disabled");
      }
    });
  });
});

給用戶一個復選框以選擇他們是否要更新

如果用戶沒有表明他們想要更新他們的密碼,另一種選擇是從表單中刪除密碼字段。 這是CodePen上的示例。

$(".password-fields").each(function () {
  var $fields, $button, html;
  $fields = $(this);
  $button = $fields.prev(".update-password").find("input");
  html = $fields.html();

  $button
    .on("change", function () {
      if ( $(this).is(":checked") ) {
        $fields.html(html);
      }
      else {
        $fields.html("");
      }
    })
    .prop("checked", "checked")
    .click();
});

在任何一種情況下,它都不需要更新應用程序本身。 您只是提交要更改的字段。

我改為覆蓋#password_required? 用戶模型中的方法。

class User < ActiveRecord::Base
  devise :database_authenticatable, :validatable #, ...

  def password_required?
    if respond_to?(:reset_password_token)
      return true if reset_password_token.present?
    end
    return true if new_record?
    password.present? || password_confirmation.present?
  end
end

因此,如果用戶是新用戶,他將需要指定密碼。 但是,僅當用戶填寫 password 或 password_confirmation 屬性時,才要求存在用戶指定密碼。

有關更多詳細信息,請參閱: https : //github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb#L33

我的實現與原始實現幾乎相同: https : //github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb#L53

除了我檢查是否存在(對於空白字符串返回 false)

這是關於我對此事的拉取請求的討論: https : //github.com/plataformatec/devise/pull/3920

任何在 2020 年訪問的人,這是我找到的最簡單的解決方案:

registrations_controller.rb

class RegistrationsController < Devise::RegistrationsController

  protected

  def update_resource(resource, params)
    # Require current password if user is trying to change password.
    return super if params["password"]&.present?

    # Allows user to update registration information without password.
    resource.update_without_password(params.except("current_password"))
  end
end

在routes.rb中:

devise_for :users, controllers: {
  registrations: 'users/registrations'
}

全部歸功於: Masatoshi Nishiguchi

https://www.mnishiguchi.com/2017/11/24/rails-devise-edit-account-without-password/

如果您希望設計僅在用戶嘗試更改密碼時檢查當前密碼(這意味着您可以在不提供當前密碼的情況下更改其他屬性):

class RegistrationsController < Devise::RegistrationsController

  protected

    def update_resource(resource, params)
      if params[:password].blank? && params[:password_confirmation].blank?
      resource.update_without_password(params)
    else
     super
    end
  end
end

同樣在您的模型中:

attr_accessor :current_password

不要忘記

devise_for :users, controllers: {registrations: 'registrations'}

routes.rb 中

這是更多的業務邏輯,所以我不會在控制器中放入太多代碼。 我建議覆蓋Devise::Models::Validatable#password_required? 在您的模型中:

def password_required?
  new_record? || password.present? || password_confirmation.present?
end

作為您的用戶模型中的解決方法

def password_required?
  encrypted_password.blank? || encrypted_password_changed?
end

對於未來的 Google 員工,我只花了 3 個小時,這對我有用。

在我的用戶模型中,我刪除了 :validatable 並添加了密碼和 password_confirmation 的驗證方法,僅當它們存在時:

class User < ApplicationRecord
  devise :database_authenticatable, :registranable, recoverable, :rememberable
    
  validates :password, length: { in: 6..128 }, if: lambda {self.password.present?}
  validates_confirmation_of :password, if: lambda {self.password.present?}
end

然后在我的 users_controller 的更新方法中,如果密碼和 password_confirmation 參數為空,我將刪除它們

class UsersController > ApplicationController
  def update
    if params[:user][:password].blank?
      params[:user].delete(:password)
      params[:user].delete(:password_confirmation)
    end

    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to @user, notice: 'User was successfully updated' }
      else
        format.html { render :edit }
      end
    end
  end
end

使用 Devise 復雜的做事方式,簡單、朴素、輕松且不會手忙腳亂。

不客氣。

我遇到了完全相同的問題,這是我想出的解決方案,相信我是可行的。 我所做的是創建第二個user_params方法並將其命名為user_params_no_pass無論如何看看:這里發生的事情是管理員將在需要更新密碼時提供密碼,否則將密碼留空。 當密碼為空的user_params_no_pass使用其他user_params 我希望有幫助

    def update

    if @user.valid_password?(params[:user][:password])
          respond_to do |format|

            if @user.update(user_params)
              format.html { redirect_to @user, notice: 'User profile was successfully updated.' }
              format.json { render :show, status: :ok, location: @user }
            else
             format.html { render :new }
             format.json { render json: @user.errors, status: :unprocessable_entity }
            end
          end
    else
      respond_to do |format|

            if @user.update(user_params_no_pass)
              format.html { redirect_to @user, notice: 'User profile was successfully updated without password.' }
              format.json { render :show, status: :ok, location: @user }
            else
              format.html { render :edit }
              format.json { render json: @user.errors, status: :unprocessable_entity }
            end
          end
    end
  end

  def destroy
     @user.destroy
      respond_to do |format|
        format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
        format.json { head :no_content }
      end
  end
  private

    def set_user
      @user = User.find(params[:id])
    end

    def user_params
      params.require(:user).permit(:user_name, :first_name, :middle_name, :last_name, :dob, :gender, :race, :hispanic, :leader, :mentor, :student, :email, :organization_id, :password, :opus,
  :release_date, :days_to_release, :current_level, :is_active)
    end


    def user_params_no_pass
      params.require(:user).permit(:user_name, :first_name, :middle_name, :last_name, :dob, :gender, :race, :hispanic, :leader, :mentor, :student, :email, :organization_id, :opus,
  :release_date, :days_to_release, :current_level, :is_active)
    end

我發現上面代碼的這種細微變化更容易理解:

def update
  @user = User.find(params[:id])

  method = if user_params[:password].blank?
             :update_without_password
           else
             :update_with_password

  if @user.send(method, user_params)
    redirect_to @user, notice: 'User settings were saved.'
  else
    render :edit
  end
end

在對上述可能性進行了大量探索之后,我終於找到了一個解決方案,它允許您無需密碼即可更新某些屬性,而有些則使用:

我為 user/edit.html.erb 創建了一個視圖,其中包含以下表單。

<%= form_for(@user) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <%= f.label :name %>
  <%= f.text_field :name, class: 'form-control' %>

  <%= f.submit "Save changes"%>
<% end %>

我在 routes.rb 中設置了路由

resources :users, only: [:show, :index, :edit, :update]

我在 users_controller.rb 中做了編輯和更新方法

def edit
  @user = current_user
end

def update
  @user = current_user
  if @user.update_attributes(user_params)
    flash[:success] = "Profile updated"
    redirect_to root_url
  else
    render 'edit'
  end
end


def user_params
  params.require(:user).permit(:name, :avatar, :whatsup)
end

我使用此編輯視圖進行不需要密碼的更改。 它完全跳過設計注冊控制器,因為我鏈接到它

edit_user_path(current_user)

我還設置了參數,以便無法在此處更改電子郵件和密碼。 要更新密碼和電子郵件,我鏈接到股票生成的設備視圖:

edit_user_registration_path(current_user)

我承認這是一項巨大的工作,但沒有一個更簡單的解決方案可以解決上述所有問題。

更好更短的方法:將檢查參數添加到 user_params 塊中。 例如:

def user_params
    up = params.require(:user).permit(
      %i[first_name last_name role_id email encrypted_password
         reset_password_token reset_password_sent_at remember_created_at
         password password_confirmation]
    )

    if up[:password].blank? && up[:password_confirmation].blank?
      up.delete(:password)
      up.delete(:password_confirmation)
    end
    up
end

要更新用戶的屬性,您需要使用:current_password,檢查“您的用戶是否使用正確的 current_password 或有人想要破壞您的用戶”

所以形式上:

= f.input :current_password,
          hint: "we need your current password to confirm your changes",
          required: true,
          input_html: { autocomplete: "current-password" }

在控制器中:

    if your_user.valid_password?(params[:user][:current_password])
        your_user.update_attributes(user_params.except(:current_password, :password, :password_confirmation))
    end

第一行檢查'你的用戶是否發送了正確的密碼',然后我們可以更新屬性而不是垃圾

如果你真的想在沒有 current_password 的情況下更新密碼,那么你需要使用用戶reset_password方法

@user.reset_password(params[:user][:password], params[:user][:password_confirmation])

這是適用於所有場景的問題的完整工作解決方案:

  if params.dig(:user, :password).blank?
    updated = @user.update_without_password(params[:user].to_unsafe_hash)
  else
    if params.dig(:user, :current_password).nil?
      @user.reset_password(params[:user][:password], params[:user][:password_confirmation])

      updated = @user.update_without_password(params[:user].to_unsafe_hash)
    else
      updated = @user.update_with_password(params[:user].to_unsafe_hash)
    end

    bypass_sign_in(@user)
  end

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM