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.
Related
Playing around with authentication from scratch and for some reason my users email address is not being saved to the DB. When I fire up the console and pull a random user, their email is always nil, but have validations set for the presence of email and am get a successful user created message. This was working before and not sure what broke it. I know this is something super simple but it has got me for the last few hours and am hoping a second pair of eyes can tell me what I'm doing wrong. GitHub link below
[https://github.com/smarandache1990/auth][1]
<h1>Sign Up</h1>
<%= 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 %>
<p>
<%= f.label :email %><br />
<%= f.text_field :email %>
<% #user.errors.full_messages_for(:email).each do |message| %>
<div><%= message %></div>
<% end %>
</p>
<p>
<%= f.label :password %><br />
<%= f.password_field :password %>
<% #user.errors.full_messages_for(:password).each do |message| %>
<div><%= message %></div>
<% end %>
</p>
<p>
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
<% #user.errors.full_messages_for(:password_confirmation).each do |message| %>
<div><%= message %></div>
<% end %>
</p>
<p class="button"><%= f.submit %></p>
<% end %>
<h1>Sign In</h1>
<%= form_tag sessions_path do %>
<p>
<%= label_tag :email %><br />
<%= text_field_tag :email, params[:email] %>
</p>
<p>
<%= label_tag :password %><br />
<%= password_field_tag :password %>
</p>
<p class="button"><%= submit_tag "Log in" %></p>
<% end %>
class User < ApplicationRecord
attr_accessor :email, :password, :password_confirmation
before_save :encrypt_password
validates :password, confirmation: true, on: :create
validates :password_confirmation, presence: true
#validates_presence_of :password, :on => :create
#validates_confirmation_of :password
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
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
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to log_in_path, :notice => "Signed up!"
else
render :new, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
class SessionsController < ApplicationController
def new
end
def create
#user = User.authenticate(params[:email], params[:password])
if #user
session[:user_id] = #user.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now[:notice] = "Wrong username or password"
render :new, status: :unprocessable_entity
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "Logged out!"
end
end
Remove the email from the attr_acessor (here).
attr_accessor creates the getters and setters for all attributes listed there. So, basically, you're overriding these methods for the email attribute.
About the validation, use this:
validates :email, presence: true, uniqueness: true
This is my view:
<%=form_for [:admin, #user] do |f|%>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<%=f.label :name %>
<%=f.text_field :name %>
<%=f.label :email %>
<%=f.text_field :email %>
<%=f.label :password %>
<%=f.password_field :password %>
<%=f.label :password_confirmation %>
<%=f.password_field :password_confirmation%>
<%=f.submit "Submit" %>
<%end%>
Controller code for adding user:
def create
#user = User.new(user_params)
if #user.save
redirect_to admin_users_path
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password)
end
These are validations in the model:
validates :name, presence: true
validates :email, presence: true
validates :password, presence: true
validates :password, confirmation: { case_sensitive: true }
But confirmation password doesn't work.
Validation works for all (they are required) form elements, except second password input - password_confirmation which can be different from first password input.
User is added to the database even if second password input is empty, because in the validation rules, there is no rule for that .
What am I doing wrong ?
You need to add password_confirmation to user_params in the controller.
I.e.
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
Try: validates_confirmation_of :password
Model:
class Person < ActiveRecord::Base
validates_confirmation_of :user_name, :password
validates_confirmation_of :email_address, :message => "should match confirmation"
end
View:
<%= password_field "person", "password" %>
<%= password_field "person", "password_confirmation" %>
You could take a look to the validates_confirmation_of.
The problem I'm facing most likely has something to do with strong parameters. The thing is when I try to edit some user information and update it, an error appears which is not related with this form. That kind of error's supposed to pop up only when signing up or logging in.
For example, here is my database. And then I click 'Edit'.
After editing some information and submitting it, the error pops up.
controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to [#user, #task], notice: "Thank you for signing up!"
else
render "new"
end
end
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to #user
else
render 'edit'
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to {|format| format.js }
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
end
views/user/edit.html
<h1>Editing user</h1>
<%= form_for :user, url: #user, method: :patch do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#user.errors.count, "error") %> prohibited
this task from being saved:
</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :first_name %><br>
<%= f.text_field :first_name %>
</p>
<p>
<%= f.label :last_name %><br>
<%= f.text_field :last_name %>
</p>
<p>
<%= f.submit %>
</p>
<%= link_to 'Back to List', users_path %>
<% end %>
models/user.rb
class User < ActiveRecord::Base
has_secure_password
EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :first_name, presence: true,
length: {maximum: 20}
validates :last_name, presence: true,
length: {maximum: 40}
validates :email, presence: true,
format: {with: EMAIL_REGEX},
uniqueness: {case_sensitive: false}
validates :password,
length: {within: 6..40}
has_many :tasks
end
What do I do? Can anybody help?
The problem is not with the strong params. It is due to validation you have on password.
This
validates :password,length: {within: 6..40}
should be
validates :password,length: {within: 6..40}, on: :create
Yes, the problem is in strong params. You should never permit password and password_confirmation
def user_params
params.require(:user).permit(:first_name, :last_name, :email)
end
Just remove password and password_confirmation from user_params. You wouldn't want to mass assign those values.
And this would be a very bad practice to store a plain password in database. You should store encrypted or hashed password in database; it would enhance the security and integrity of your application.
To get more information about storing an encrypted or hashed password, please take a look at this and this links.
Working through the railstutorial.org . Currently on the Update Profile page part of it. When leaving the Password and Password Confirmation fields empty, only the Password is too short error comes up, though in the tutorial screenshot Password confirmation can't be blank message is present. But, it does show up when the Password field is filled and Password Confirmation field is left empty.
edit.html.erb :
<% provide(:title, "Edit user") %>
<h1>Update your profile</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
<% end %>
<%= gravatar_for #user %>
change
</div>
</div>
users_controller.rb:
.
.
.
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
.
.
.
user.rb:
class User < ActiveRecord::Base
has_secure_password
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }
before_create :create_remember_token
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.digest(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
self.remember_token = User.digest(User.new_remember_token)
end
end
Your User.rb model is out of sync with the one MHartl uses at that point in the tutorial. Specifically, notice that he has an explicit validates :password_confirmation, presence: true in addition to the validates :password, length: { minimum: 6 }. When you call #user.update_attributes, it hits these validators and, in his case, both fail, whereas in your User.rb model there is not the presence validator.
When you have the password field filled in, you're hitting validators defined in has_secure_password rather than in your model, which is why they appear then.
Make sure you include the password and password_confirmation in your allowed parameters in you controller:
private
def user_params
params.required(:user).permit(:name, :email, password,:password_confirmation)
end
EDIT:
As the other had already pointed out you miss the validation of password_confirmation
validates :password_confirmation, presence: true
I'm new to rails and i want to make a signup login and forget password functionality from scratch so i have been following railscast 250 and 274 episodes..so the problem is, the validations on password and password confirmation fields are working fine on signup form but on the reset password form those validations are not working at all..code is below
model:
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :signup_token
attr_accessor :password
before_save :encrypt_password
before_create {generate_token(:auth_token)}
before_create {generate_token(:signup_token)}
validates :password, length: {minimum: 8}, confirmation: true, presence: true,:on => :create
validates :email, presence: true, uniqueness: true
validates :email, format: {with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, message: "not valid"}
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt) && user.active == true
user
else
nil
end
end
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
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
end
password_resets_controller:
class PasswordResetsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to root_url, :notice => "Email sent with password reset instructions."
end
def edit
#user = User.find_by_password_reset_token!(params[:id])
end
def update
#user = User.find_by_password_reset_token!(params[:id])
if #user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Password reset has expired."
elsif #user.update_attributes(params[:user])
redirect_to root_url, :notice => "Password has been reset."
else
render :edit
end
end
end
and views under password reset are
new.html.erb:
<h1>Reset Password</h1>
<%= form_tag password_resets_path, :method => :post do %>
<div class="field">
<%= label_tag :email %>
<%= text_field_tag :email, params[:email] %>
</div>
<div class="actions"><%= submit_tag "Reset Password" %></div>
<% end %>
edit.html.erb:
<h1>Reset Password</h1>
<%= form_for #user, :url => password_reset_path(params[:id]) 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 %>
<div class="field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
<div class="actions"><%= f.submit "Update Password" %></div>
<% end %>