LoginSignup
26
27

More than 5 years have passed since last update.

deviseで、パスワードを登録していないユーザーはパスワード追加、登録しているユーザーはパスワードを更新する

Last updated at Posted at 2014-11-08

OmniAuthなどを使っていて、

  • OmniAuthで外部サービスと連携している場合はパスワード不要
    • パスワード不要でも、パスワード追加、更新は可能
  • OmniAuthで外部サービスと連携していない場合はパスワード必須

のような状況で、Deviseでのパスワード変更機能を作った。

Deviseでログイン機能を作ると、edit_user_registration_pathが自動に作られるはずなので、そこをカスタマイズする。

link_to('パスワードを変える', edit_user_registration_path(@user))

パスワード変更の入力フォームは以下のように書く。

registrations.html.slim
.main
  .container
    .row
      .span4.offset4
        h2
          | パスワード変更
          = resource_name.to_s.humanize
        = form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f|
          = devise_error_messages!
          - if current_user.encrypted_password.present?
            div
              = f.label :current_password, '現在のパスワード'
              br
              = f.password_field :current_password
          div
            = f.label :password, '新しいパスワード'
            br
            = f.password_field :password, :autocomplete => 'on'
          div
            = f.label :password_confirmation, '新しいパスワードをもう一度'
            br
            = f.password_field :password_confirmation
          div
            = f.submit '更新する'

User情報を更新するコントローラメソッドはDevise::RegistrationsControllerのupdate。

registration_controller.rb
 # PUT /resource
  # We need to use a copy of the resource because we don't want to change
  # the current user in place.
  def update
    self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
    prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)

    if update_resource(resource, account_update_params)
      yield resource if block_given?
      if is_flashing_format?
        flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ?
          :update_needs_confirmation : :updated
        set_flash_message :notice, flash_key
      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

# ...

protected

# ...

 # By default we want to require a password checks on update.
  # You can overwrite this method in your own RegistrationsController.
  def update_resource(resource, params)
    resource.update_with_password(params)
  end

その中でも、実際に更新を行っているのは if update_resource(resource, account_update_params) の部分。

なので、deviseを使っているモデルでupdate_resourceをoverrideして、もしもまだパスワードを登録していないときは、パスワードを確認せずにアップデートして、パスワードを登録しているときはパスワードを確認してアップデートする。

registrations_controller.rb

class RegistrationsController < Devise::RegistrationsController
  before_filter :configure_permitted_parameters, if: :devise_controller?

 #override
  def update_resource(resource, params)
    resource.update_with_password_if_password_present(params)
  end

  def configure_permitted_parameters
     devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:email, :pen_name, :password, :password_confirmation, :current_password) }
  end

このように、DeviseのRegistrationsControllerを継承した先でoverride。

user.rb
class User < ActiveRecord::Base

# 省略  

devise :database_authenticatable, :registerable,
    :recoverable, :rememberable, :trackable, :validatable

# 省略

  def update_with_password_if_password_present(params)
    if encrypted_password.present?
      return update_with_password(params)
    else
      result = update_attributes(params)
      clean_up_passwords
      return result
    end
  end

なお、DatabaseAuthenticatableのupdate_with_passwordの実装は以下のようなもの。

database_authenticatable.rb
      # Update record attributes when :current_password matches, otherwise returns
      # error on :current_password. It also automatically rejects :password and
      # :password_confirmation if they are blank.
      def update_with_password(params, *options)
        current_password = params.delete(:current_password)

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

        result = if valid_password?(current_password)
          update_attributes(params, *options)
        else
          self.assign_attributes(params, *options)
          self.valid?
          self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
          false
        end

        clean_up_passwords
        result
      end

参考

26
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
27