How to verify password exists using client_side_validations gem? - ruby-on-rails

I have a change password form that requires a user to provide their current password. Using the client_side_validations gem, is it possible to verify that the current password is correct using a remote validator/javascript? If so, how?
app/views/passwords/new.html.haml
= simple_form_for #password_form, url: passwords_path, method: :post, validate: true do |f|
= f.input :original_password
= f.input :new_password
= f.input :new_password_confirmation, required: true
= f.submit 'Save'
app/forms/password_form.rb
...
attr_accessor :original_password, :new_password
validate :verify_original_password
validates_presence_of :original_password, :new_password
validates_confirmation_of :new_password
validates :new_password, password: true
def verify_original_password
unless #user.authenticate(original_password)
errors.add :original_password, "is not correct"
end
end
...

You can't validate password correctness client-side without passing the client an enormous list of all the (presumably) hashed passwords in your DB. This is computationally not really feasible, but even worse you would be exposing a list of all your users' hashed passwords! It wouldn't be hard for a hacker to break open at least a few of these (I've heard stories of people decrypting >80% of a list of MD5 hashed passwords in <1 hour).
So you'll have to speak to the server to verify a password.

Related

Do form validation that does not use database

I have a form and I have made some inputs required. After submitting the form that value will be sent to an API. I know that the validations are put into model file but since I do not have a database, how can I use the rails validation?
Right now I am validating the code inside a controller using if else.
if !params[:groups][:name].blank? && !params[:groups][:make].blank? && !params[:groups][:model].blank? && !params[:groups][:firmware].blank?
This does the work but it is not very elegant.
Take a look at ActiveModel, it lets you do "model things" without the database. There were some limitations that made me not use it in the end (I think related to associations) but for simple stuff it's great (and it's a part of how ActiveRecord works.
Example code from docs
class Person
include ActiveModel::Model
attr_accessor :name, :age
validates :name, :age, presence: true
end
this is easy. On the form input fields that you NEED, add required: true For example:
<%= form.for #something do |f| %>
<%= f.text_field :title, placeholder: 'Title', required: true %>
<% end %>
The user gets an error if the required fields are not filled out correctlty.
Is this what you mean?
Justin
EDIT
I guess I would look at using the gem
client_side_validations
Let us know how you go

simple_form doesnt prevent sending request without required fields

i have this form
= simple_form_for #category.fields.build, url: category_fields_path(#category) do |f|
= f.input :kind, collection: Field::FIELD_TYPES, prompt: "Choose field type"
= f.input :description
= f.submit "Add field"
and this field model
class Field < ActiveRecord::Base
FIELD_TYPES = %w(integer float date string text)
validates :description, presence: true
validates :kind, presence: true
belongs_to :category
end
when i leave 'description' field empty, no request is send and i get notice 'Please fill out this field'. which is what i want. on the other hand, when description is filled in but kind is not, a request is still send to the 'create' action! No field gets created, but 'description' needs to be filled in again. there should be no request in such situation. any idea how to fix this?
Though, I don't have exact answer to Your problem, but You should start with checking HTML output. Simple from relies on HTML5 to provide front-end validation. All inputs should have required attribute, to have validation enabled. Maybe there is a bug, and in this particular case simple_form does not output required attribute.
Another thing to take in account as it is HTML5, consult browser support: http://caniuse.com/#feat=form-validation . Theoretically it's possible that You are testing on browser that has limited support for form validations.
If You discover that simple_from did not output required for Your kind fuel, try forcing it:
= f.input :kind, collection: Field::FIELD_TYPES, prompt: "Choose field type", required: true
I got my answer at Simple Form github's issue topichere. to sum up, problem was prompt, validation is not (yet?) working with it correctly, solution is to replace it, eg like this:
= f.input :kind, collection: Field::FIELD_TYPES, include_blank: "Choose field type", label: false

Validate field is unique compared to another field in same form

Say I have two fields in a new or edit form:
<%= f.text_field :email %>
<%= f.text_field :parent_email %>
How, in my model, can I validate that parent_email is different from email? The exclusion option seems like it might work, but I can't figure out how to access the email field's value within the model. Do I need to implement this in the controller instead?
validates :parent_email, exclusion: self.email # doesn't work, nor does :email
The following should work (but I guess there are cooler solutions out there):
class User
validate :email_differs_from_parent_email
private
def email_differs_from_parent_email
if email == parent_email
errors.add(:parent_email, "parent_email must differ from email")
end
end
end

Rails - update_attributes coming up against validations

So I've got a user model, with login, email address, password, password confirmation, name, avatar (picture), etc. There are validations on the first 5, basically stating that all 5 need to exist in order to create a new model.
However, this causes problems for me where updates are concerned.
I've got an edit page, where the user can only edit their name and avatar. I'm not currently intending to let them change their login, and I wish to do an email and password change from a different page.
So the edit form looks like this:
<% form_for #user, :html => { :multipart => true } do |u| %>
<p>
<label>Name:</label>
<%= u.text_field :name %>
</p>
<p>
<label>Avatar:</label>
<%= display_user_avatar %>
<%= u.file_field :avatar%>
</p>
<p>
<%= submit_tag %>
</p>
<% end %>
If I attempt to do a #user.update_attributes(params[:user]), then because the only 2 params are name and avatar, the update fails, since stuff like password, password confirmation, email, etc are required to validate the entry, and they simply don't exist in that form.
I can get around this by doing #user.update_attribute(:name, params[:user][:name]), but then I worry about whether avoiding validations is a Good Thing™ or not. Especially with regards to something like password updates, where I do need to validate the new password.
Is there another way?
And if I were to do this simply using update_attribute for :name and :avatar, how would I go about doing it?
Would this work?
params[:user].each do |attribute|
#user.update_attribute(attribute, params[:user][attribute])
end
Is this an acceptable way to do this...?
--edit as follow up --
Okie, I tried as you suggested and did
def update
#user = User.find_by_login(params[:id])
if #user.update_attributes!(params[:user])
redirect_to edit_user_path(#user)
else
flash[:notice] = #user.errors
redirect_to edit_user_path(#user)
end
end
So it's doing the ! version, and the exception caught & displayed in the browser is:
Validation failed: Password is too short (minimum is 5 characters)
The info in the server log is:
Processing UsersController#update (for 127.0.0.1 at 2010-07-18 11:56:59) [PUT]
Parameters: {"user"=>{"name"=>"testeeeeee"}, "commit"=>"Save changes", "action"=>"update", "_method"=>"put", "authenticity_token"=>"BMEGRW/pmIJVs1zlVH2TtZX2TQW8soeCXmMx4kquzMA=", "id"=>"tester", "controller"=>"users"}
Urm. Looking at this, I just realised that it is submitting "id"=>"tester". Now, I have my routes set up so that it is showing the users login name, instead of the user_id... Could that be why? It is attempting to find a update a user with user_id == tester, but since it doesn't exist, it attempts to create one instead?
Is it actually something I'm doing wrong due to the route?
Hmmm... rake routes tells me that the route is:
edit_user GET /users/:id/edit(.:format) {:action=>"edit", :controller=>"users"}
PUT /users/:id(.:format) {:action=>"update", :controller=>"users"}
And I set up the route like that in the user.rb file:
def to_param
"#{login}"
end
but it's definitely been displaying login instead of id all this time. But I'm also doing right at the beginning of the update action, a #user = User.find_by_login(params[:id]), and then updating that #user.
I'm very confused. >.<
Second update:
My User.rb validation stuff are as follows:
validates_length_of :login, :within => 3..20
validates_length_of :password, :within => 5..20
validates_presence_of :login, :email, :password, :password_confirmation, :salt, :name, :on => :create
validates_uniqueness_of :login, :case_sensitive => false
validates_confirmation_of :password
validates_format_of :email, :with => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :message => "format is invalid."
attr_accessor :password, :password_confirmation
And the hashed_password section is here:
def password=(pass)
#password = pass
self.salt = User.random_string(10) if !self.salt?
self.hashed_password = User.encrypt(#password, self.salt)
end
u.attributes gives me
>> u.attributes
=> {"salt"=>"NHpH5glxsU", "name"=>"test er", "avatar_updated_at"=>nil, "updated_at"=>Sat Jul 17 07:04:24 UTC 2010, "avatar_file_size"=>nil, "avatar_file_name"=>nil, "hashed_password"=>"84f8675c1ed43ef7f8645a375ea9f867c9a25c83", "id"=>1, "avatar_content_type"=>nil, "login"=>"tester", "email"=>"tester#tester.com", "created_at"=>Fri May 07 10:09:37 UTC 2010}
Urmmm... Ok, so it's what you said, about the virtual attribute password being actually nonexistent...
So how do I get around that?
Bugger, here I thought I was being smart fiddling with my own authentication code...
How easy is it to change to one of those authentication plugins? Will I need to create a new User model? Or should the plugin be able to work with my current one?
Thanks for all the help so far, btw! :D
I've checked this and a partial update of just 2 attributes via update_attributes works fine. All the other attributes are left with their previous values, meaning that the validation shouldn't fail. A couple of things to try:
In your controller action are you loading the user via User.find? i.e. are you starting from a valid model.
Are you sure the update is failing due to validation errors? Try replacing the update_attributes with update_attributes!. The latter will throw an exception if the update fails due to validation. Or check #user.errors after the attempted update to confirm which validation has failed.
Update
If User.find_by_login doesn't find a matching record it will return nil and won't create a new record for you. Is it possible that the tester user in the database has a password that is too short? Maybe that user was created before you put the validations in your code? Are you using any kind of plugin or callback to encrypt user passwords before saving the records? Is password actually a virtual attribute that isn't saved and the actual password is in a field like encrypted_password?
Try this from script/console (use the same environment as you are testing the app with - development or production)
> user = User.find_by_login 'tester'
> user.valid?
> user.attributes
The user.valid? will return true of false and will tell you whether the user is valid to start with, before you even try an update.
Update 2 (fixing the validation)
In terms of fixing your own code, you could add a method like the following to your User model:
def password_validation_required?
hashed_password.blank? || !#password.blank?
end
and then update all your password related validation rules so that they only apply if this method returns true e.g.
validates_length_of :password, :within => 5..20,
:if => :password_validation_required?
What this is saying is only do the password validation rule if we don't yet have a hashed_password (on a new user for example) or if a new plain text password has been specified via password=. If the user already has a password and it is being left unchanged then skip the password validation.
You are right to be considering using a plugin though. Writing your own authentication code can be an interesting excercise and can be required if you have some unusual requirements. The down side is that there can be security issues that you haven't thought of. Retrofitting something like restful_authentication to your app shouldn't be too bad. You might just need to rename one or two fields on your User model.

How to validate user input in RoR?

I know that the RoR can do the validation in the models. But I want to confirm the user's password in the views. I means, it show two textfield for user to type password twice to ensure the user type the password correct, but in the database I only store one value. How can I handle it in the RoR?
In your model do:
validates_confirmation_of :password
In your view do:
<%= form.password_field :password %>
<%= form.password_field :password_confirmation %>
This is using the built in rails confirmation validation. It's will add the virtual accessor for you.

Resources