web-dev-qa-db-fra.com

Devise mettre à jour l'utilisateur sans mot de passe

Je souhaite mettre à jour les attributs des utilisateurs sans mot de passe dans le système. Le cas est comme, si les champs mot de passe et confirmation de mot de passe ne sont pas vides alors j'ai besoin d'une erreur de conception et s'ils sont vides alors les autres attributs d'utilisateur doivent être mis à jour. Comment est-ce que je pourrais faire ceci avec la devise?

Merci d'avance! 

70
kriysna

Est-ce ce que vous cherchez? Du wiki Devise

Autoriser les utilisateurs à modifier leur compte sans fournir de mot de passe

Il montre comment faire pour Rails 3 et Rails 4

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

La solution de John est une alternative plus simple

61
Dty

Je pense que c'est une bien meilleure solution:

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

Cela vous évite de devoir modifier le contrôleur Devise en supprimant simplement le champ de mot de passe de la réponse au formulaire s'il est vide.

Veillez simplement à utiliser ce before@user.attributes = params[:user] ou ce que vous utilisez dans votre action update pour définir les nouveaux paramètres du formulaire.

83
John

Je pense qu'avec Devise 3.2+, vous pouvez simplement remplacer update_resource dans votre contrôleur sous-classé. Voici un exemple de la question posée à l'origine:

class MyRegistrationsController < Devise::RegistrationsController
  protected

  def update_resource(resource, params)
    resource.update_without_password(params)
  end
end
18
Duncan Robertson

J'ai résolu ce problème comme suit: si le formulaire est soumis avec un mot de passe, vous devez remplir le champ mot_de_passe ou le formulaire mis à jour sans mot de passe ni mot_de_passe

dans le modèle:

class User
  devise: bla-bla-bla

  attr_accessor :current_password
end

Dans le contrôleur:

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
7
Mikhail Grishko

Je résous ce problème avec mon interface. Cela peut aller de deux manières.

Désactiver les champs s'ils sont vides

Ce bit de jQuery désactive les champs avant que le formulaire ne soit soumis au cas où ils seraient vides. Il faut un certain modèle de balisage, consultez la démo de 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");
      }
    });
  });
});

Donner à l'utilisateur une case à cocher pour indiquer s'il souhaite mettre à jour

Une autre option consiste à supprimer les champs de mot de passe du formulaire si l'utilisateur n'a pas indiqué qu'il souhaitait mettre à jour son mot de passe. Voici l'exemple sur 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();
});

Dans les deux cas, l'application ne nécessite aucune mise à jour. Vous ne faites que soumettre les champs que vous souhaitez modifier.

3
rb-

Je remplace plutôt #password_required? méthode à l'intérieur du modèle d'utilisateur.

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

Ainsi, si l'utilisateur est nouveau, il devra spécifier le mot de passe . Cependant, l'utilisateur ne devra spécifier le mot de passe que s'il remplit les attributs password ou password_confirmation.

Pour plus de détails, voir: https://github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb#L33

Mon implémentation est presque identique à l'original: https://github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb#L53

sauf que je vérifie la présence (qui renvoie false pour les chaînes vides)

Voici la discussion à propos de ma demande de retrait à ce sujet: https://github.com/plataformatec/devise/pull/3920

3
graudeejs

Si vous souhaitez toujours prendre en charge le changement de mot de passe mais le rendre facultatif, vérifiez la disponibilité de current_password en tant que tel: 

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

Ainsi, si le mot de passe current_password est présent, vous pouvez continuer et mettre à jour le mot de passe, sinon l'ignorer et le mettre à jour sans mot de passe.

2
Sebyddd

params [: utilisateur] [: mot de passe] ne fonctionne pas dans Rails 6

Vous devez changer params [: utilisateur] [: motdepasse] en params [: motdepasse]

Supprimer [: utilisateur] dans tous les paramètres

mon code source est ci-dessous

avant d'exécuter cette commande

Les rails génèrent des informations: contrôleurs utilisateurs -c = inscriptions

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
1
Changwoo Rhee

Si vous voulez que Devise vérifie le mot de passe actuel uniquement lorsque l'utilisateur tente de changer de mot de passe (cela signifie que vous pouvez autres modifications sans fournir le mot de passe actuel):

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

Aussi dans votre modèle:

attr_accessor :current_password

Et n'oublie pas 

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

dans routes.rb .

1
Maxim Kachanov

Je trouve cette légère variation du code ci-dessus plus facile à suivre:

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
0
sigvei

Après une longue exploration des possibilités ci-dessus, j'ai finalement trouvé une solution qui vous permet de mettre à jour certains attributs sans mot de passe et d'autres avec:

J'ai créé une vue pour user/edit.html.erb avec le formulaire suivant.

<%= 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 %>

Je mets des routes dans routes.rb

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

J'ai créé et mis à jour des méthodes dans 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

J'ai utilisé cette vue de modification pour les modifications ne nécessitant pas de mot de passe. Il ignore complètement le contrôleur d’enregistrement de carte, car je me connecte à celui-ci avec

edit_user_path(current_user)

J'ai également paramétré les paramètres pour que l'email et le mot de passe ne puissent pas être changés ici. Pour mettre à jour le mot de passe et l'adresse e-mail, je fais un lien vers la vue d'ensemble générée par le stock:

edit_user_registration_path(current_user)

J'admets qu'il s'agit d'un travail énorme, mais aucune des solutions les plus simples n'a résolu tous les problèmes ci-dessus.

0
James Mosier

Meilleure façon: ajoutez les paramètres de vérification dans le bloc user_params. Par exemple:

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
0
Andriy Kondzolko

J'ai eu exactement le même problème et c'est la solution que j'ai trouvée et croyez-moi que cela fonctionne . Ce que j'ai fait est de créer une deuxième méthode user_params et l'ai nommée user_params_no_pass. fournissez un mot de passe lorsque le mot de passe doit être mis à jour, sinon laissez le mot de passe vide. Lorsque le mot de passe est vide, le user_params_no_pass est utilisé, sinon user_params. J'espère que ça aide

    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
0
Idriss

C'est plus une logique métier donc je ne mettrais pas trop de code dans le contrôleur. Je suggère de remplacer Devise::Models::Validatable#password_required? dans votre modèle:

def password_required?
  new_record? || password.present? || password_confirmation.present?
end
0
Artur Bilski

pour mettre à jour les attributs pour l'utilisateur, vous devez utiliser: current_password, pour cocher la case "votre utilisateur utilise-t-il correctement le mot de passe actuel ou quelqu'un veut-il le casser"

donc sous forme:

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

dans le contrôleur:

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

la première ligne vérifie "votre utilisateur envoie-t-il le mot de passe correct ou non", et ensuite nous pourrons mettre à jour les attributs sans faille

Solution de contournement dans votre modèle utilisateur

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