I created the New/Edit User's page so Admin can add and update Users. Now what I am trying is this: when the the Admin edits a User, the Admin has the option of not updating the password, only the email. But how will I retain the password after saving the updated email?
Edit Page:
<div><%= f.label :email, 'Email Address' %>
<%= f.email_field :email, autofocus: true, :class => "form-control", autocomplete: "off", :placeholder => "Enter email address" %></div><br />
<div><%= f.label :password %>
<%= f.password_field :password, autocomplete: "off", :placeholder => "Enter password" %></div><br />
<div><%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, autocomplete: "off", :placeholder => "Confirm password" %></div><br />
Model:
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation
Controller:
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
redirect_to edit_user_path
flash[:notice] = "User updated."
else
render :action => 'edit'
end
end
You can check if the password field is empty or not, if it's empty than use devise's update_without_password method, try this
def update
#user = User.find(params[:id])
if params[:user][:password].blank?
if #user.update_without_password(params[:user].except(:password, :password_confirmation))
redirect_to edit_user_path
flash[:notice] = "User updated."
else
render :action => 'edit'
end
else
if #user.update_attributes(params[:user])
redirect_to edit_user_path
flash[:notice] = "User updated."
else
render :action => 'edit'
end
end
end
Related
I'm having issues with Devise. I'm trying to set up an authentication scheme in Rails and I'm getting an "invalid email or password" response when I try to sign in. This is right after creating a user, logging in, logging out, and then trying to log in again. I'm totally sure that the email/password match, so that's not the issue. I'm using Devise 4.4.3 and Rails 5.2.0. Help a guy out, please!
Users controller:
class UsersController < ApplicationController
before_action :authenticate_user!
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:notice] = "You've successfully signed up!"
else
flash[:alert] = "There was a problem signing up."
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
Sessions controller:
class SessionsController < ApplicationController
before_action :authenticate_user!
def create
#user = User.authenticate(params[:email], params[:password])
if #user
flash[:notice] = "You've signed in."
session[:user_id] = #user.id
redirect_to "/"
else
flash[:alert] = "There was a problem signing in. Please try again."
redirect_to signin_path
end
end
end
User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :posts
has_many :projects
attr_accessor :password
validates_confirmation_of :password
validates :email, :presence => true, :uniqueness => true
before_save :encrypt_password
def encrypt_password
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password,password_salt)
end
def self.authenticate(email, password)
user = User.find_by "email = ?", email
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
end
<h2>Log in</h2>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<% if devise_mapping.rememberable? -%>
<div class="field">
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
</div>
<% end -%>
<div class="actions">
<%= f.submit "Log in" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
Routes.rb
Rails.application.routes.draw do
devise_for :users
root :to => 'homes#index'
resources :posts
resources :projects
resources :users
end
New session view:
<h2>Log in</h2>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<% if devise_mapping.rememberable? -%>
<div class="field">
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
</div>
<% end -%>
<div class="actions">
<%= f.submit "Log in" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
Happy to provide more if needed. I'm still somewhat new to development so pardon me if it's a very obvious answer. Thanks!
In the user model try this
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
I'm filling out the sign up form on my site. I type in first name, last name, email, password, and password confirmation. When I click "submit", my form clears, and the console tells me the user already exists (even though it doesnt, and throws me a success message). In short: I'm trying to create a new user, and save it to my database.
See below:
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE
"users"."firstname" IS NULL LIMIT 1 (0.1ms) rollback transaction
Rendered users/new.html.erb within layouts/application (2.4ms)
Completed 200 OK in 136ms (Views: 68.4ms | ActiveRecord: 0.9ms)
See code below - cheers!
users_controller.rb
class UsersController < ApplicationController
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "You signed up successfully"
flash[:color] = "valid"
redirect_to #user
else
flash[:notice] = "Form is invalid"
flash[:color] = "invalid"
render "new"
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
def show
#user = User.find(params[:id])
end
def new
end
end
user.rb
class User < ActiveRecord::Base
validates_length_of :password, :in => 6..20, :on => :create
validates :password_confirmation, presence: true, if: -> { new_record? || changes["password"] }
end
new.html.erb
<%= form_for(:user, :url => {:controller => 'users', :action => 'create'}) do |f| %>
<%= render 'shared/error_messages' %>
</br> <%= f.text_field :firstname, placeholder: 'First Name' %>
</br> <%= f.text_field :lastname, placeholder: 'Last Name' %>
</br> <%= f.text_field :email, placeholder: 'Email' %>
</br> <%= f.password_field :password, placeholder: 'Password' %>
</br> <%= f.password_field :password_confirmation, placeholder: 'Confirm Password' %>
<%= f.submit :Register %>
<% end %>
As you can see from the generated SQL, firstname is nil on your new user.
That is because this line contains typos:
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation
should be:
params.require(:user).permit(:firstname, :lastname, :email, :password, :password_confirmation
I think you are missing some params. Using devise? Try to create a new user via rails console to see what parameters are needed.
Check the strong parameters given as first_name and last_name but in form it is firstname and lastname.
I set the Devise to allow an email only registration process that sends an email with a link to confirm account by setting password.
I would like to set the firstname and lastname at the same time than password setup (and account confirmation) but I don't know how to do.
Here is my confirmation controller (working), Crafter being my standard User.
I tried Crafter.update_attributes(params[:crafter]) with no luck.
Any help would be wonderful. TY.
def update
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
#confirmable.attempt_set_password(params[:crafter])
if #confirmable.valid?
#confirmable.update_attributes(params[:crafter])
do_confirm
else
do_show
#confirmable.errors.clear #so that we wont render :new
end
else
self.class.add_error_on(self, :email, :password_allready_set)
end
end
if !#confirmable.errors.empty?
render 'devise/confirmations/new' #Change this if you don't have the views on default path
end
end
def do_show
#confirmation_token = params[:confirmation_token]
#requires_password = true
self.resource = #confirmable
render 'devise/confirmations/show' #Change this if you don't have the views on default path
end
def do_confirm
#confirmable.confirm!
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, #confirmable)
end
And my "confirm account" form :
<%= form_for resource, :as => resource_name, :url => update_user_confirmation_path, :html => {:method => 'patch', class: "form-horizontal", role: "form"}, :id => 'activation-form' do |f| %>
<%= f.text_field :firstname, autofocus: true, placeholder: "Firstname", class: "form-control" %>
<%= f.text_field :lastname, placeholder: "Lastname", class: "form-control" %>
<% if #requires_password %>
<%= f.password_field :password, autofocus: true, placeholder: "Choose A Password", class: "form-control" %>
<%= f.password_field :password_confirmation, placeholder: "Confirm Password", class: "form-control" %>
<% end %>
<%= hidden_field_tag :confirmation_token,#confirmation_token %>
It's hard to tell but I'd guess you're not getting the firstname, lastname params passed to the controller.
Rails 4 uses something called strong parameters; the README there has lots of good info if you want to go beyond my example below.
In your ApplicationController you need to allow for additional params, add these lines:
app/controllers/application_controller.rb
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) do |u|
u.permit(:firstname, :lastname, :email, :current_password, :password, :password_confirmation)
end
end
That should fix things.
In your CrafterController you could do something like this:
#confirmable.update_with_password(account_update_params)
Just do be a bit cleaner
I have settings form. I want make so that if password field is empty, update other attributes.
Also I'm using devise. Are there some useful methods that might help me with this?
form.html.erb
<%= form_for(#user, :url => url_for(:controller => 'settings', :action => 'update')) do |f| %>
<%= f.label :email, 'Email' %>
<%= f.email_field :email %>
<%= f.label :mobile, 'Mobile' %>
<%= f.phone_field :mobile %>
<%= f.label :current_password, "Current password" %>
<%= f.password_field :current_password %>
<%= f.label :password, "New Password" %>
<%= f.password_field :password, :autocomplete => "off" %>
<%= f.label :password_confirmation, "Confirm New Password" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Update" %>
<% end %>
user.rb
validates :password, length: {minimum: 4}, on: :update, allow_blank: true
settings_controller.rb
def update
#user = current_user
if #user.update_attributes(user_params)
sign_in(#user, :bypass => true)
flash[:success] = "Settings were successfully updated"
respond_to do |format|
format.html {redirect_to :action=> :show}
end
else
flash[:fail] = "Settings **was not updated**"
respond_to do |format|
format.html {render :action => :show }
end
end
end
Use this:
def update
# For Rails 4
account_update_params = devise_parameter_sanitizer.sanitize(:account_update)
# For Rails 3
# account_update_params = params[:user]
# 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 his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
This is shameless copy-paste from devise wiki, it contains a lot of useful information that you can refer to - wiki
I am also using devise and in the admin namespace having a users_controller inherited from application_controller, as I wanted to keep the admin and user section separate.
The following thing worked for me.
In the User model
validates :password, :password_confirmation, presence: true, on: :create
In update action of the controller
def update
# .....
#user.update_without_password(users_params)
# ......
end
I'm not sure how to validate the confirm password element, this code seems to work, but the validates confirmation of password doesn't seem to compare the passwords as expected, just need a hand getting used to Rails 4's new ways.
Sign Up View:
Sign Up
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.text_field :email, class: "form-control", autofocus: "", required: "", placeholder: "Email address" %>
<%= f.password_field :password, class: "form-control", required: "", placeholder: "Password" %>
<%= f.password_field :password_confirmation, class: "form-control", required: "", placeholder: "Confirm Password" %><br/>
<p class="button"><%= submit_tag "Register", class: "btn btn-lg btn-primary btn-block" %></p>
<% end %>
</div>
Users Controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.create(user_params)
if #user.save
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :passord_confirmation, :salt, :encrypted_password)
end
end
Users Model:
class User < ActiveRecord::Base
attr_accessor :password
before_save :encrypt_password
validates_presence_of :password, on: :create
validates :password, confirmation: :true
validates_presence_of :email
validates_uniqueness_of :email
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
private
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
It looks like your model's password confirmation may be off, unless you have your own private method confirmation
Try changing your model from validates :password, confirmation: :true to validates_confirmation_of :password
The API has some examples as well.
You need to make the password and password_confirmation accessible. For example:
attr_accessor :password
attr_accessible :password, :password_confirmation
validates :password, :presence => true,
:confirmation => true
Try adding that, and see if it makes a difference.