How to separate change password from devise form - ruby-on-rails

I am trying to do two things:
1) Change the default "edit user form" - provided with devise - to remove "password" and allow the other fields to be updated without having to enter a password ie remove the default validation for password.
2) Create a separate form for changing password
I have got everything to work, there is only one problem, in the separate form for updating password, I have included a field for current password. When using the form, no validation is made for current password, so I changed
#user.update_attributes(params[:user])
to
#user.update_with_password(params[:user])
This worked, however it raised another issue. Back in the main form with all the other details except password, the form now asks for a "current password". How can I achieve this without a validation for current password being called on the main form?
here is my registrations controller:
def update
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
clean_up_passwords(resource)
respond_with_navigational(resource) do
if params[:change_password] # or flash[:change_password]
render :change_password
else
render :edit
end
end
end
end
Thanks!
Solution 1
I have found a solution to the problem (albeit a very messy one):
def update
#user = User.find(current_user.id)
if params[:user][:password].blank?
if #user.update_attributes(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
respond_with_navigational(resource) do
render :edit
end
end
else
if #user.update_with_password(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
clean_up_passwords(resource)
respond_with_navigational(resource) do
render :change_password
end
end
end
Solution 2
Can you suggest a better solution?

Did you bother to check out Devise wiki? There are examples for both this cases
https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password
https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-password
You should be looking at #user.update_with_password(params[:user]) vs #user.update_attributes(params[:user])

The accepted answer does not fully address the question. Which, I believe is to have a separate form for user profile attributes (like email, first name, etc) vs. the password. Here's what you need to do for that:
First, leverage the Devise::RegistrationsController for your profile updates.
Customize the view and remove the password and password_confirmation fields. Devise ignores these if they are not present in the put.
If you don't want to require the current password to make profile changes, read this. Not recommended; not secure.
Second, create your own controller to manage the password updates and your own helper to require current_password, password, and password_confirmation on update.
class PasswordsController < ApplicationController
before_filter :authenticate_user!
def edit
#user = current_user
end
def update
#user = User.find(current_user.id)
if #user.update_password_with_password(user_params)
# Sign in the user by passing validation in case their password changed
sign_in #user, :bypass => true
redirect_to edit_password_path, flash: { success: "Successfully updated password" }
else
render "edit"
end
end
private
def user_params
params.require(:user).permit(:current_password, :password, :password_confirmation)
end
end
Here's the helper, update_password_with_password that will require the new password fields.
class User < ActiveRecord::Base
def update_password_with_password(params, *options)
current_password = params.delete(:current_password)
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
end

Related

Custom Update Function for Devise. Requiring current password only when updating password

I currently have a user that has extra attributes that I want them to be able to update without entering their current password.
So I had to create my own RegistrationsController and override the update method. (Taken from Devise github page).
def update
account_update_params = devise_parameter_sanitizer.sanitize(:account_update)
# required for settings form to submit when password is left blank
if account_update_params[:password].blank?
account_update_params.delete('password')
account_update_params.delete('password_confirmation')
end
#user = User.find(current_user.id)
if #user.update_attributes(account_update_params)
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case their password changed
sign_in #user, :bypass => true
redirect_to edit_user_registration_path
else
render 'edit'
end
end
But now, I want to require them to put in their correct current password if they want to change their password. This seems to be an issue because if they do put in their current password, I have to add it to the permitted parameters. But then it tells me that current_password is not a valid attribute of the user.
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:name, :email, :attr1, :attr2,
:password, :password_confirmation) }
end
Use update_with_password and update_without_password mehotds
with_password = params[:current_password].present?
if #user.update_user(with_password)
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case their password changed
sign_in #user, :bypass => true
redirect_to edit_user_registration_path
else
render 'edit'
end
private
def update_user(with_password)
if with_password
#user.update_with_passoword(account_update_params)
else
#user.update_passoword(account_update_params)
end
end
See documentation

How to allow users to edit their account without providing a password?

I'm using Rails 4 and Devise 3.2.4 for authentication.
I'm trying to allow users to update their account (eg: name, email....etc ) without providing password.
I have followed this tutorial -> https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password
After doing what the tutorial said, I am able to to update the user without providing password but when I want to change the "password" itself, I don't need to provide password confirmation and current password.
How do I allow users to change first name, last name without providing password and when changing password, user needs to type password, password confirmation and current password.
Please see below my code.
Thanks.
===Update==
My code doesn't seem to work if I have :validatable in my User Model
==
registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def update
account_update_params = devise_parameter_sanitizer.sanitize(:account_update)
if account_update_params[:password].blank?
account_update_params.delete("password")
account_update_params.delete("password_confirmation")
end
#user = User.find(current_user.id)
if #user.update_attributes(devise_parameter_sanitizer.sanitize(:account_update))
set_flash_message :notice, :updated
sign_in #user, :bypass => true
redirect_to #user
else
render "edit"
end
end
protected
end
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) { |u|
u.permit(:firstname, :lastname, :password, :password_confirmation )}
end
end
Replace your registration controller by this code
class RegistrationsController < Devise::RegistrationsController
def update
#user = User.find(current_user.id)
successfully_updated = if needs_password?(#user, params)
#user.update_with_password(devise_parameter_sanitizer.sanitize(:account_update))
else
# remove the virtual current_password attribute
# update_without_password doesn't know how to ignore it
params[:user].delete(:current_password)
#user.update_without_password(devise_parameter_sanitizer.sanitize(:account_update))
end
if successfully_updated
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
private
# check if we need password to update user data
# ie if password or email was changed
# extend this as needed
def needs_password?(user, params)
user.email != params[:user][:email] ||
params[:user][:password].present?
end
end
With this code, you will require to put in password when changing password and email and will not require password when changing any other information

RoR : Are devise controllers generated automatically or should we write on our own?

I am new to ruby on rails. I am trying to use devise gem for authentication. i am going through the tutorials in github. I have created devise views using rails generate devise:views. But i dont find any controllers. Do i need to create on my own or is there any command to generate controllers for it?
Plz help
Devise already creates the required controllers for you behind the scenes. Few of these controllers are: RegistrationController, SessionController.
To customize or override any controller, say RegistrationController; you can do following (snippet from my one application):
class RegistrationsController < Devise::RegistrationsController
before_filter :admin_user, :only => [:destroy]
def new
super
end
def create
if simple_captcha_valid? #verifying user registration by captcha
super
else
build_resource
clean_up_passwords(resource)
flash.now[:alert] = "There was an error with the captcha code below. Please re-enter the code."
render :new
end
end
def update
# required for settings form to submit when password is left blank
if params[:user][:password].blank?
params[:user].delete("password")
params[:user].delete("password_confirmation")
end
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
redirect_to rooth_path
end
end
For more you can follow: https://github.com/plataformatec/devise#configuring-controllers

[Rails]update_without_password doesn't update user info

I executed following code
#user = User.find(current_user.id)
successfully_updated = if needs_password?(#user, params)
#user.update_with_password(params[:user])
else
# remove the virtual current_password attribute update_without_password
# doesn't know how to ignore it
params[:user].delete(:current_password)
#user.update_without_password(params[:user])
end
if successfully_updated
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
but update_without_password give false and database is rollbacked.
Do I have to do something for update_without_password?
I just went through my own issues with this. The following code should work as advertised (as found on the wiki page for Devise).
def update
#user = User.find(current_user.id)
successfully_updated = if needs_password?(#user, params)
#user.update_with_password(params[:user])
else
params[:user].delete(:current_password)
#user.update_without_password(params[:user])
end
if successfully_updated
set_flash_message :notice, :updated
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
Make sure you also define the private method for 'needs_password?'
def needs_password?(user, params)
(params[:user].has_key?(:email) && user.email != params[:user][:email])
|| !params[:user][:password].blank?
end
My issue was that I had removed the "email" field from the form in the 'edit.html.erb' file, so the 'needs_password?' method kept returning true since user.email was never equal to nil. To fix the issue, I added in a check params[:user].has_key?(:email) to see if 'email' exists in the hash.
FWIW, the 'update_without_password' pretty much works the same way as 'update_with_password' except that it strips out the 'password' and 'password_confirmation' parameters before calling 'update_attributes' on the object, so you shouldn't have to do anything more. Try looking upstream a bit and see if you really are calling 'update_without_password'.

How to show notice if user update account is not successfull( devise gem )?

I overrided a little devise gem to allow user change some details with or without account depends on situation.
I don't want to use devise error messages, because I'm using jQuery validation plugin.
I want to show some notice error, when some devise errors exists, but I can't get where I need to put flash[:error].
Here is my update action:
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
if params[:user][:password].present?
clean_up_passwords resource
render "edit_user/edit_password"
end
end
So, can someone help me with this ?
If you are using Jquery validation have you enabled this on your views by doing something similar <%= form_for #user, :validate => true, do |f|%>
Update
You could do something somewhat similar to this
def update_password
#user = current_user
#current_password = params[:user][:current_password]
#password = params[:user][:password]
if #user.valid_password?(#current_password)
if #current_password == #password
redirect_to user_path(#user)
flash[:error] = "Current password cannot be the same as your new password."
else
if #user.update_attributes(params[:user])
redirect_to login_path
flash[:success] = "Password has been changed."
else
redirect_to user_path(#user)
flash[:error] = "Didn't save contact an administrator."
end
end
else
redirect_to user_path(#user)
flash[:error] = "Current password is incorrect."
end
end
Providing you add the top bit I suggested into your view and have installed Jquery validation correctly this should work

Resources