hello,
i am writing a simple user login system.
when registering a user account, i have two field:
password
password_confirmation
which are validated using
validates_presence_of
validates_confirmation_of
and these are then used to generate a password hash which is stored in
my database
when i want to update the record (without changing the password and
entering new values for password and password_confirmation), using the
update_attributes method, i am getting errors thrown up by this
validation.
i want to be able to update the record, even if the user does not
specify a new password
thanks
Jon H. wrote:
maybe my post wasn’t too clear.
basically, what i am after is a way of selectively validating certain
fields of the model, i.e.
when creating a new object and saving it to the database, i want to
validate the presence of username, password and password_confirmation,
and validate confirmation of password
but, when updating the user’s profile (name, address etc) i want to
ignore the password validation (i’ll have a seperare form for changing
password)
hope that’s clearer
Could you post the code that you are trying that throws errors?
Post the code where you try to update fields and save the query. Also,
post a few segments on the password stuff from the model.
-Ben L.
maybe my post wasn’t too clear.
basically, what i am after is a way of selectively validating certain
fields of the model, i.e.
when creating a new object and saving it to the database, i want to
validate the presence of username, password and password_confirmation,
and validate confirmation of password
but, when updating the user’s profile (name, address etc) i want to
ignore the password validation (i’ll have a seperare form for changing
password)
hope that’s clearer
Ben L. wrote:
Jon H. wrote:
maybe my post wasn’t too clear.
basically, what i am after is a way of selectively validating certain
fields of the model, i.e.
when creating a new object and saving it to the database, i want to
validate the presence of username, password and password_confirmation,
and validate confirmation of password
but, when updating the user’s profile (name, address etc) i want to
ignore the password validation (i’ll have a seperare form for changing
password)
hope that’s clearer
Could you post the code that you are trying that throws errors?
Post the code where you try to update fields and save the query. Also,
post a few segments on the password stuff from the model.
-Ben L.
I’m running the same scheme, and here’s how I update the name of a user:
@thisUser = User.find_by_id(session[:user_id])
@thisUser[0].name = “new name!!!”
@thisUser[0].update()
@thisUser[0].save
An easier way to do this would be
User.update(session[:user_id], {:name => ‘new name!!!’})
it shouldn’t throw any errors about passwords, unless you have it rigged
so it checks the password each time you try to update the entry.
Jon H. wrote:
hi ben
i have tried your method of updating, and it’s throwing up the password
validation problems again. i probably haven’t rigged up the validation
correctly for what i am trying to do.
here is the relevant code from my user model:
class User < ActiveRecord::Base
attr_accessor :password, :password_confirmation
attr_protected :admin, :enabled, :password_salt, :password_hash
validates_uniqueness_of :username
validates_presence_of :username, :email, :password,
:password_confirmation
validates_confirmation_of :password
def before_create
salt = [Array.new(6){rand(256).chr}.join].pack(“m”).chomp
self.password_salt = salt
self.password_hash = User.hash_password(self.password, salt)
end
def after_create
@password = nil
@password_confirmation = nil
end
…
end
and then in my user management controller here is the update method:
def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
flash[:notice] = ‘User was successfully updated.’
redirect_to :action => ‘show’, :id => @user
else
render :action => ‘edit’
end
end
my understanding of update_attributes was to update and validate only
those supplied in params[:user] which afaik comes from my update form,
and therefore does not have the password and password_confirmation
fields
thanks
the first thing that jumps out at me is:
validates_presence_of :username, :email, :password,
:password_confirmation
then you have
def after_create
@password = nil
@password_confirmation = nil
end
So when you create, it validates that there’s a password. After you
have created your user with a password, it sets the password to nil.
Once you try to update a field, it does the validation methods again,
and sure enough validates_presence of :password and
:password_confirmation fail.
I think that’s what it is - lemme know.
-Ben L.
i don’t actually have password and password_confirmation fields in my
database, since i’m just storing the hash and salt obviously. so when i
load the record from the database using find() given the user id, these
are set to nil anyway.
the code i am using is based on that given in the ‘agile web development
with ruby’ book, and it sets the plaintext password to nil to prevent it
from being stored in session data (when i get round to that)
any suggestions?
hi ben
i have tried your method of updating, and it’s throwing up the password
validation problems again. i probably haven’t rigged up the validation
correctly for what i am trying to do.
here is the relevant code from my user model:
class User < ActiveRecord::Base
attr_accessor :password, :password_confirmation
attr_protected :admin, :enabled, :password_salt, :password_hash
validates_uniqueness_of :username
validates_presence_of :username, :email, :password,
:password_confirmation
validates_confirmation_of :password
def before_create
salt = [Array.new(6){rand(256).chr}.join].pack(“m”).chomp
self.password_salt = salt
self.password_hash = User.hash_password(self.password, salt)
end
def after_create
@password = nil
@password_confirmation = nil
end
…
end
and then in my user management controller here is the update method:
def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
flash[:notice] = ‘User was successfully updated.’
redirect_to :action => ‘show’, :id => @user
else
render :action => ‘edit’
end
end
my understanding of update_attributes was to update and validate only
those supplied in params[:user] which afaik comes from my update form,
and therefore does not have the password and password_confirmation
fields
thanks
def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
flash[:notice] = ‘User was successfully updated.’
redirect_to :action => ‘show’, :id => @user
else
render :action => ‘edit’
end
end
change to:
def update
def update
@user = User.find(params[:id])
add these three lines
@user.name = params[:user].name <— note down below
@user.update()
if @user.save
or this line
if User.update(params[:id], {:name => params[:user].name})
Note: that line is assuming your form contains this
<%= text_field ‘user’, ‘name’ %>
-Ben L.
Ben L. wrote:
def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
flash[:notice] = ‘User was successfully updated.’
redirect_to :action => ‘show’, :id => @user
else
render :action => ‘edit’
end
end
change to:
def update
def update
@user = User.find(params[:id])
add these three lines
@user.name = params[:user].name <— note down below
@user.update()
if @user.save
or this line
if User.update(params[:id], {:name => params[:user].name})
Note: that line is assuming your form contains this
<%= text_field ‘user’, ‘name’ %>
-Ben L.
the problem is that you’re using update_attributes vs. update_attribute.
update_attribues
“Updates all the attributes from the passed-in Hash and saves the
record. If the object is invalid, the saving will fail and false will be
returned.”
that params[:user] you’re passing it obviously only has this field:
<%= text_field ‘user’, ‘name’ %>
if it had:
<%= text_field ‘user’, ‘name’ %>
<%= text_field ‘user’, ‘password’ %>
<%= text_field ‘user’, ‘password_confirmation’ %>
then that syntax would have been fine, because it would have done
update_attribues and it would have passed all of the validates
requirements.
if you’re delving into agile, you should keep
http://api.rubyonrails.com/ handy. it’s important to know how to use it
- it can really save you time. I have a copy of it downloaded through
Web Devil (for OSX) so I can program and get any syntax help I need
without the internet.
the problem with update_attribute is that, however, the attribute
updated is not subjest to any validation. so, for example, it would be
easy to make the mistake of updating the ‘username’ to either nil or
perhaps another username whihc already exists.
i guess what i’m trying to do if update a subset of fields, performing
validation on only those fields.
thanks
Jon H. wrote:
the problem with update_attribute is that, however, the attribute
updated is not subjest to any validation. so, for example, it would be
easy to make the mistake of updating the ‘username’ to either nil or
perhaps another username whihc already exists.
i guess what i’m trying to do if update a subset of fields, performing
validation on only those fields.
thanks
then just use update_attribute not update_attributes - that should be
exactly what you need.
On 7/18/06, Ben L. [email protected] wrote:
thanks
You can restrict the validation to occur only on create. That way, when
the
user is updated it won’t trip the validation trip switch.
validate_presence_of :password, :on => :create
validates_confirmation_of :password, :on => :create
Alternatively, if you need more control use the :if option to point to a
proc or a method to test if validation should occur.
validate_presence_of :password, :if => Proc.new{ |model| model.something
a_condition }
or
validate_presence_of :password, :if =>
:some_method_that_evaluates_to_true_or_false
Ben L. wrote:
then just use update_attribute not update_attributes - that should be
exactly what you need.
i have tried this, but validation checks are not performed on the
attribute being updated. for example i can update the username to
either nil or to another existing username
Daniel ----- wrote:
You can restrict the validation to occur only on create. That way, when
the
user is updated it won’t trip the validation trip switch.
validate_presence_of :password, :on => :create
validates_confirmation_of :password, :on => :create
Alternatively, if you need more control use the :if option to point to a
proc or a method to test if validation should occur.
validate_presence_of :password, :if => Proc.new{ |model| model.something
a_condition }
or
validate_presence_of :password, :if =>
:some_method_that_evaluates_to_true_or_false
ok i’ll give that kinda thing a try. thanks
Daniel ----- wrote:
You can restrict the validation to occur only on create. That way, when
the
user is updated it won’t trip the validation trip switch.
validate_presence_of :password, :on => :create
validates_confirmation_of :password, :on => :create
Alternatively, if you need more control use the :if option to point to a
proc or a method to test if validation should occur.
validate_presence_of :password, :if => Proc.new{ |model| model.something
a_condition }
or
validate_presence_of :password, :if =>
:some_method_that_evaluates_to_true_or_false
so, is the best way of doing this to have a couple of flags in my user
model
updating_profile
updating_password
for example, which are set by the update methods in my controller, and
to use validation in my user model something like?
validates_presence_of :password, :password_confirmation :if =>
updating_password == true
sorry, you can probably tell this is my first time using ruby on rails!
is it possible to control whether the validates_* statements are
executed directly depending on the method in the controller through
which the model fields are being updated?
Alan F. wrote:
Jon H. wrote:
when i want to update the record (without changing the password and
entering new values for password and password_confirmation), using the
update_attributes method, i am getting errors thrown up by this
validation.
I think adding
:on => :create
to the end of your validates_* lines will restrict the validation to
occur only on create, not update.
Alan
yeah, that should do it for now - but i think when i come to create a
form allowing the user to update their password, then i’ll need that
password validation to kick in. i think i need to use :if =>
something
Jon H. wrote:
when i want to update the record (without changing the password and
entering new values for password and password_confirmation), using the
update_attributes method, i am getting errors thrown up by this
validation.
I think adding
:on => :create
to the end of your validates_* lines will restrict the validation to
occur only on create, not update.
Alan
On 7/19/06, Jon H. [email protected] wrote:
proc or a method to test if validation should occur.
http://lists.rubyonrails.org/mailman/listinfo/rails
One really good place to have a look at this kind of code is
techno-weenies
acts_as_authenticated. It really is a good plugin for this. Very small
and
light weight. Doesn’t get in your way.
http://svn.techno-weenie.net/projects/plugins/acts_as_authenticated/generators/authenticated/templates/model.rb
He uses a method password_required?
A quick extract of the pertinent bits of the user model
Virtual attribute for the unencrypted password
attr_accessor :password
validates_presence_of :password, :if =>
:password_required?
validates_presence_of :password_confirmation, :if =>
:password_required?
validates_length_of :password, :within => 4…40, :if =>
:password_required?
validates_confirmation_of :password, :if =>
:password_required?
before_save :encrypt_password
Encrypts some data with the salt.
def self.encrypt(password, salt)
Digest::SHA1.hexdigest(“–#{salt}–#{password}–”)
end
Encrypts the password with the user salt
def encrypt(password)
self.class.encrypt(password, salt)
end
protected
# before filter
def encrypt_password
return if password.blank?
self.salt =
Digest::SHA1.hexdigest(“–#{Time.now.to_s}–#{login}–”)
if new_record?
self.crypted_password = encrypt(password)
end
def password_required?
crypted_password.blank? || !password.blank?
end
Hope that gets you started.
On Mon, Jul 17, 2006 at 06:47:59PM +0200, Jon H. wrote:
hello,
i am writing a simple user login system.
did you try the LoginGenerator (or any of the alternative login systems
available) ?
see
http://wiki.rubyonrails.org/rails/pages/HowToQuicklyDoAuthenticationWithLoginGenerator
and
http://wiki.rubyonrails.org/rails/pages/LoginGenerator
Even if none of the solutions mentioned there does fit your needs, they
might provide a good starting point.
Jens
Daniel ----- wrote:
One really good place to have a look at this kind of code is
techno-weenies
acts_as_authenticated. It really is a good plugin for this. Very small
and
light weight. Doesn’t get in your way.
thanks Daniel, i’ll give those technique’s a try