0

I have an auth system from scratch, and when a user clicks on 'edit profile' it has to input the current password no matter the field he wants to edit.

def update
  if params[:user][:password].present?
    authenticated = @user.authenticate(params[:user][:current_password])
    if authenticated && @user.update(user_params)
      redirect_to root_url
      flash[:notice] = "Your profile was successfully updated!"
    else
      @user.errors.add(:current_password, 'is invalid') unless authenticated
      render :edit
    end
  elsif @user.update(user_params)
    redirect_to root_url
    flash[:notice] = "Your profile was successfully updated!"
  else
    render :edit
  end
end

How can I call authenticate or use some context model validation only for the scenario when the user wants to change his password?

hhh_
  • 310
  • 2
  • 5
  • 17
Bogdan Popa
  • 1,099
  • 1
  • 16
  • 37

2 Answers2

1

I wouldn't recommend mixing this logic into the model because you end up with complexity that is hard to follow as your application grows over time.

Try taking a look into form objects:

I'd implement something like this:

class UserUpdateForm
  include ActiveModel::Model

  # Attributes
  attr_accessor :user, :new_password, :new_password_confirmation

  # Validations
  validates :current_password, if: :new_password
  validate :authenticate, if: :current_password
  validates :new_password, confirmation: true, allow_blank: true

  def initialize(user)
    self.user = user
  end

  def submit(params)
    self.new_password = params[:new_password]
    self.new_password_confirmation = params[:new_password_confirmation]

    if self.valid?
      # Set other attributes as needed, then set new password below.
      self.user.password = self.new_password if self.new_password.present?
      self.user.save
    else
      false
    end
  end

private

  def authenticate
    unless self.authenticate(self.current_password)
      self.errors.add(:current_password, 'is invalid')
    end
  end
end

Then you can call it from your controller like so:

def update
  @user_update_form = UserUpdateForm.new(@user)

  if @user_update_form.submit(params)
    flash[:notice] = "Your profile was successfully updated!"
    redirect_to root_url
  else
    render :edit
  end
end

See the links above for how to handle the view and such. This is just to get you started.

Chris Peters
  • 17,918
  • 6
  • 49
  • 65
  • Sure looks good but how can I take a step further in order to skip authenticating the user if no password fields are modified? I edited my question but the code looks pretty ugly. – Bogdan Popa May 20 '15 at 16:31
  • Can I do something like having a hidden boolean field which sets to true when any password field is completed or the password fields are toggled down(I hide them and toggle with jquery), and validate based on the boolean value? – Bogdan Popa May 20 '15 at 16:32
  • I didn't read the last statement in your question well enough. I thought you wanted to require the current password no matter what. I'll try updating my answer. – Chris Peters May 20 '15 at 18:08
0

You may create a nested if-else in this action statement that will check for existence of new_password and new_password_confirmation (or whatever the new password and confirmation fields are called) in the params[:user] object. If they are present - you may redirect to some king of page with request to enter existent password.

Another way is to use ajax to show asynchronously the dialog box with the same request (like respond_with self-invoking javascript function that handles that). Then handle submit button in of the dialog in the other action of the controller.

Update (considering use of validators):

Considering validation you may write your own validator (for password) and condition to check when the new password field come with some data from the client.

I think it could look like this:

validate :password_update?

def password_update?
    if new_password.present?
        if current_password !== self.password
          errors.add(:current_password, "must be supplied!")
        else
            # update data and password
        end
    else
        # do your regular update
    end
end
zmii
  • 4,123
  • 3
  • 40
  • 64