Entwickeln Sie einen Update-Benutzer ohne Passwort

77

Ich möchte Benutzerattribute ohne Passwort im Gerät aktualisieren. Der Fall ist wie folgt: Wenn Passwort und Passwortbestätigungsfelder nicht leer sind, muss ich einen Fehler entwickeln, und wenn sie leer sind, müssen andere Benutzerattribute aktualisiert werden. Wie könnte ich das mit Geräten machen?

Danke im Voraus!

Kriysna
quelle

Antworten:

64

Ist es das, wonach du suchst? Aus dem Devise-Wiki

Ermöglichen Sie Benutzern das Bearbeiten ihres Kontos ohne Angabe eines Kennworts

Es zeigt, wie es für Schienen 3 und Schienen 4 geht

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

Johns Lösung ist eine einfachere Alternative

Dty
quelle
1
José Valim hat seitdem viele Informationen in diesem Wiki gelöscht , um Benutzern zu empfehlen, ihre eigenen Controller zu schreiben und Methoden in geerbten Devise::RegistrationsControllers nicht zu überschreiben . Man kann sich den Wiki-Verlauf für frühere Versionen ansehen , aber seinen Rat berücksichtigen.
Abe Voelker
93

Ich denke, das ist eine viel bessere Lösung:

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

Dies verhindert, dass Sie den Devise-Controller ändern müssen, indem Sie einfach das Kennwortfeld aus der Formularantwort entfernen, wenn es leer ist.

Stellen Sie einfach sicher, dass Sie dies vor @user.attributes = params[:user] oder was auch immer Sie in Ihrer updateAktion verwenden, um die neuen Parameter aus dem Formular festzulegen.

John
quelle
Perfekt. Genau das, wonach ich gesucht habe
Karlingen
4
wo legst du das hin
Alexander Presber
5
@MoMolog Folgen Sie dem ersten Teil des Devise-Tutorials: github.com/plataformatec/devise/wiki/… Erstellen Sie einfach einen leeren benutzerdefinierten Registrierungscontroller. Kopieren Sie anschließend die Aktualisierungsfunktion von hier aus auf den Standard-Devise-Registrierungs-Controller: github.com/plataformatec/devise/blob/master/app/controllers/… Platzieren Sie Johns Code oben auf Ihrem Controller. Ersetzen Sie "update_resource (resource, account_update_params)" durch "@ user.update_attributes (account_update_params)"
onichase
kannst du es in das Modell setzen? @onichase
ahnbizcad
4
Diese Lösung wird nicht berücksichtigt current_password. Es wurde für den Fall entwickelt, dass alle Benutzerattribute (Name, E-Mail usw.) zusätzlich zu den Feldern zum Ändern des Kennworts angezeigt werden . Ohne eine solche Logik lässt Devise keine Aktualisierung von Attributen zu, wenn der Benutzer das Formular sendet, nachdem er andere Felder geändert hat, während die Kennwortfelder leer bleiben.
John
18

Ich denke, mit Devise 3.2+ können Sie update_resource in Ihrem untergeordneten Controller einfach überschreiben. Hier ist ein Beispiel für die ursprünglich gestellte Frage:

class MyRegistrationsController < Devise::RegistrationsController
  protected

  def update_resource(resource, params)
    resource.update_without_password(params)
  end
end
Duncan Robertson
quelle
Diese Antwort, die sie alle regiert, ist mit Abstand die einfachste ab 2015.
Asciant
3
Stellen Sie einfach sicher und aktualisieren Sie Ihre Routen:devise_for :users, controllers: {registrations: 'registrations'}
Dylan Little
Ok für mich, aber ich musste auch attr_accessor :current_passwordmein Benutzermodell erweitern. Art von Hacks, die ich nicht mag. Sollte ich?
Masciugo
Sie können einfach params.delete :current_passwordvorresource.update_without_password(params)
sebsonic2o
Es ist auch offiziell dokumentiert, wie github.com/heartcombo/devise/wiki/…
woto
8

Ich habe dieses Problem wie folgt gelöst: Wenn das Formular mit einem Kennwort gesendet wird, muss das Feld current_password ausgefüllt oder das Formular ohne Kennwort und current_password aktualisiert werden

im Modell:

class User
  devise: bla-bla-bla

  attr_accessor :current_password
end

In der Steuerung:

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
Mikhail Grishko
quelle
1
Diese Antwort ist am vollständigsten und die Lösung ist die beste
Diego Somar
4

params [: user] [: password] funktioniert nicht in Schienen 6

Sie müssen params [: user] [: password] in params [: password] ändern.

Entfernen Sie [: user] in allen Parametern

Mein Quellcode ist unten

bevor Sie diesen Befehl ausführen

Schienen erzeugen Gerät: Controller Benutzer -c = Registrierungen

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
Changwoo Rhee
quelle
3

Ich löse dieses Problem mit meiner Schnittstelle. Es gibt zwei Möglichkeiten.

Deaktivieren Sie die Felder, wenn sie leer sind

Dieses Bit von jQuery deaktiviert die Felder, bevor das Formular gesendet wird, falls sie leer sind. Es erfordert ein bestimmtes Markup-Muster. Überprüfen Sie die Demo auf 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");
      }
    });
  });
});

Geben Sie dem Benutzer ein Kontrollkästchen, um auszuwählen, ob er aktualisieren möchte

Eine andere Möglichkeit besteht darin, die Kennwortfelder aus dem Formular zu entfernen, wenn der Benutzer nicht angegeben hat, dass er sein Kennwort aktualisieren möchte. Hier ist das Beispiel für 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();
});

In beiden Fällen ist kein Update der App selbst erforderlich. Sie senden nur die Felder, die Sie ändern möchten.

rb-
quelle
3

Ich überschreibe stattdessen #password_required? Methode innerhalb des Benutzermodells.

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

Wenn der Benutzer neu ist, muss er ein Passwort angeben. Der vorhandene Benutzer muss jedoch nur dann ein Kennwort angeben, wenn er entweder die Kennwort- oder die Kennwortbestätigungsattribute eingibt.

Weitere Einzelheiten finden Sie unter: https://github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb#L33

Meine Implementierung ist fast identisch mit dem Original: https://github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb#L53

außer dass ich auf Anwesenheit prüfe (was für leere Zeichenfolgen false zurückgibt)

Hier ist eine Diskussion über meine Pull-Anfrage zu diesem Thema: https://github.com/plataformatec/devise/pull/3920

graudeejs
quelle
3

Wenn Sie die Kennwortänderung weiterhin unterstützen möchten, diese jedoch optional festlegen möchten, überprüfen Sie die Verfügbarkeit current_passwordals solche:

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

Auf diese Weise können Sie, wenn das aktuelle Kennwort vorhanden ist, fortfahren und das Kennwort aktualisieren. Andernfalls können Sie es ignorieren und ohne Kennwort aktualisieren.

Sebyddd
quelle
1

Wenn Devise das aktuelle Kennwort nur überprüfen soll, wenn der Benutzer versucht, das Kennwort zu ändern ( dh , Sie können andere Attribute ändern, ohne das aktuelle Kennwort anzugeben ):

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

Auch in Ihrem Modell:

attr_accessor :current_password

Und vergiss nicht

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

in route.rb .

Maxim Kachanov
quelle
1

Es ist mehr Geschäftslogik, damit ich nicht zu viel Code in den Controller stecke. Ich schlage vor, Devise::Models::Validatable#password_required?in Ihrem Modell zu überschreiben :

def password_required?
  new_record? || password.present? || password_confirmation.present?
end
Artur Bilski
quelle
Dies ist bei weitem die beste Methode, um dies zu tun. Auch am anpassbarsten, da man genau angeben kann, für welche Attribute das Passwort benötigt wird.
Vadim
1

Als Problemumgehung in Ihr Benutzermodell einfügen

def password_required?
  encrypted_password.blank? || encrypted_password_changed?
end
römisch
quelle
1

Für zukünftige Googler habe ich gerade 3 Stunden damit verbracht. Hier ist, was für mich funktioniert hat.

In meinem Benutzermodell entferne ich: validierbare und hinzugefügte Validierungsmethode für das Passwort und die Passwortbestätigung nur, wenn sie vorhanden sind:

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

Dann lösche ich in der Aktualisierungsmethode meines users_controller die Parameter password und password_confirmation, wenn sie leer sind

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

Einfach, schlicht, leicht und ohne Ruckeln mit Devises verschlungener Art, Dinge zu tun.

Gern geschehen.

rip747
quelle
1

Jeder, der 2020 besucht, ist die einfachste Lösung, die ich gefunden habe:

in 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

In route.rb:

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

Volle Anerkennung an: Masatoshi Nishiguchi

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

Jonny
quelle
0

Ich hatte genau das gleiche Problem und dies ist die Lösung, die ich gefunden habe und glaube mir, dass es funktioniert. Was ich getan habe, ist eine zweite user_paramsMethode zu erstellen und sie user_params_no_passtrotzdem zu benennen. Schauen Sie hier: Was hier passiert, ist, dass der Administrator ein Passwort bereitstellt, wenn das Passwort aktualisiert werden muss, andernfalls lassen Sie das Passwort leer. Wenn das Passwort leer user_params_no_passist, wird das Passwort anderweitig verwendet user_params. Ich hoffe das hilft

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

Ich finde diese geringfügige Abweichung vom obigen Code leichter zu befolgen:

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
Sigvei
quelle
0

Nach eingehender Untersuchung der oben genannten Möglichkeiten habe ich endlich eine Lösung gefunden, mit der Sie einige Attribute ohne Kennwort und einige mit:

Ich habe eine Ansicht für user / edit.html.erb mit dem folgenden Formular erstellt.

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

Ich setze Routen in route.rb

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

Ich habe Bearbeitungs- und Aktualisierungsmethoden in users_controller.rb vorgenommen

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

Ich habe diese Bearbeitungsansicht für Änderungen verwendet, für die kein Kennwort erforderlich war. Der Devise Registration Controller wird komplett übersprungen, da ich mit ihm verlinke

edit_user_path(current_user)

Ich habe auch die Parameter so eingestellt, dass E-Mail und Passwort hier nicht geändert werden können. Um das Passwort und die E-Mail-Adresse zu aktualisieren, verweise ich auf die von Aktien generierte Geräteansicht:

edit_user_registration_path(current_user)

Ich gebe zu, dass dies eine große Problemumgehung ist, aber keine der einfacheren Lösungen hat alle oben genannten Probleme gelöst.

James Mosier
quelle
0

Besser und kurzer Weg: Fügen Sie dem Parameter user_params Prüfparameter hinzu. Zum Beispiel:

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

Um die Attribute für den Benutzer zu aktualisieren, müssen Sie Folgendes verwenden: current_password, um zu überprüfen, ob Ihr Benutzer das richtige current_password verwendet oder jemand Ihren Benutzer brechen möchte.

also in form:

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

im Controller:

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

In der ersten Zeile wird überprüft, ob Ihr Benutzer das richtige Kennwort sendet oder nicht. Anschließend können wir die Attribute ohne Müll aktualisieren

Игорь Хлебников
quelle