Same name action and attribute in a model

I have been following following tutorial to make a user login session. http://www.aidanf.net/rails_user_authentication_tutorial

I used the following action to encrypt the password.

def password #debugger @password1=pass self.salt = SecureRandom.hex(10) if !self.salt? self.password = User.encrypt(@password1, self.salt) end

but when it is called following error is generated.

SystemStackError (stack level too deep): app/models/user.rb:26:in password=' app/models/user.rb:27:in password=’ app/controllers/user_controller.rb:31:in new' app/controllers/user_controller.rb:31:in createuser’

I am not sure if this way of calling an action is correct or not. I need help with this one asap.

Which (if any) of those lines is line 26 in your UserController? What does the User.encrypt method look like?

Which (if any) of those lines is line 26 in your UserController? What does the User.encrypt method look like?

Ignore me... I can't even read your error message properly myself.

I used the following action to encrypt the password.

  def password     #debugger     @password1=pass     self.salt = SecureRandom.hex(10) if !self.salt?     self.password = User.encrypt(@password1, self.salt)   end

Right... so where does the value of "pass" come from? Is there a method that returns it?

In the (six year old) tutorial you're following, the method is:

  def password=(pass)     @password=pass     self.salt = User.random_string(10) if !self.salt?     self.hashed_password = User.encrypt(@password, self.salt)   end

but you've changed it to not take any attributes, and to update "self.password" rather than "self.hashed_password". It would probably help a little to post a bit more (all) of your model. Also, I'm curious why, if you're following a tutorial, would you change large chunks of the functionality? If you implement it exactly as described, does it work?

This is the complete model. I am trying to encrypt the password.

require ‘digest/sha1’

class User < ActiveRecord::Base attr_accessible :mailid, :name, :password, :username validates:name, :presence=>true validates:mailid, :presence=>true validates:username, :presence=>true

  validates:password, :presence=>true
  validates_uniqueness_of :username, :mailid
  validates_format_of :mailid, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :message => "Invalid email" 

 
  attr_accessor :passwordtext

has_many :contacts

def self.hashedpass(login, pass) u=find(:first, :conditions=>[“login = ?”, login]) return nil if u.nil? return u if User.encrypt(pass, u.salt)==u.hashed_password

nil

end

def passwordtext=(pass) #debugger @password1=pass self.salt = SecureRandom.hex(10) if !self.salt? self.password = User.encrypt(@password1, self.salt) end

def self.encrypt(pass, salt) Digest::SHA1.hexdigest(pass+salt) end end

Regards Sumit Srivastava

The power of imagination makes us infinite…

I am not exactly following the tutorial. I have customized things according to my requirements.

Regards Sumit Srivastava

The power of imagination makes us infinite…

This is the complete model. I am trying to encrypt the password.

  def self.hashedpass(login, pass)     u=find(:first, :conditions=>["login = ?", login])     return nil if u.nil?     return u if User.encrypt(pass, u.salt)==u.hashed_password     nil   end

You compare the hashed user input with the u.hashed_password attribute here...

  def passwordtext=(pass)     #debugger     @password1=pass     self.salt = SecureRandom.hex(10) if !self.salt?     self.password = User.encrypt(@password1, self.salt)   end

...but here you're setting the "password" attribute - so is there a "hashed_password" attribute on the user? And if so, how is it set?

I am not exactly following the tutorial. I have customized things according to my requirements.

Your requirement was to have a non-working password hashing process?! - as it seems that's all your customization has achieved. What was wrong with the tutorial that didn't work for you?

The self.hashedpass action is still not in use. That is not of concern right now. I need to encrypt the password chosen by user and save it. In this tutorial, it sets an attr_accessor “password” which executes the instance method “password=(pass)”. The tutorial actually uses **hashes_password ** as the attribute in table where the encrypted password is actually stored after encryption. During customizations I have used password as the column where encrypted password shall be saved. So I created my own attr_accessor, i.e., passwordtext. I have named the password_field_tag in my form as passwordtext. But when I do so, it is not executed. Instead if I rename the password_field_tag and the attr_accessor as password only, it is executed but the stackerror comes in.

Regards Sumit Srivastava

The power of imagination makes us infinite…

All of this info would have been very helpful in your OP.... anyway...

... what happens when you try this:

  def passwordtext=(pass)     @passwordtext=pass     self.salt = User.random_string(10) if !self.salt?     self.password = User.encrypt(@passwordtext, self.salt)   end

If it's still overflowing, can you post the error message again (since the model has changed since your first post this morning) and the corresponding method from the controller, as it might be something in there behaving weirdly.

This is what I receive, SystemStackError (stack level too deep): app/models/user.rb:26:in password=' app/models/user.rb:27:in password=’ app/controllers/user_controller.rb:31:in new' app/controllers/user_controller.rb:31:in createuser’ Here createuser action is used to store the user info into database. Line 31 reads as follows,@user = User.new(params[:user])

Regards Sumit Srivastava

The power of imagination makes us infinite…

I'm using

before_save :encrypt_password

  def encrypt_password     unless password.blank?       self.password_digest = BCrypt::Password.create(password)       self.password = nil       self.password_confirmation = nil     end   end

tom

User.random_string does not exists any more. So had to use SecureRandom.hex(10)

I would put a breakpoint on that line and then keep stepping into until you see where it's looping :-/

Pavling, I removed the line **self.password = User.encrypt(@password1, self.salt)**and rest worked fine. So I deduced that this might be calling password action again and going into recursive action. That’s the reason I changed my variables as explained earlier… Tom, That might be another solution. But what I am trying to know is why isn’t this method working. And how is the action **def password=(pass) ** actually being called. Because I didn’t see any exclusive line where it is being called. My analysis says it is being executed because of the attr_accessor and having name same as that of the column **password. Am I right?**Regards Sumit Srivastava

The power of imagination makes us infinite…

Which accessor? The only accessor you have is passwordtext.

Right now I have renamed it to passwordtext but originally it was password. I wrote about its behavior when named as password.

Regards Sumit Srivastava

The power of imagination makes us infinite…

Okay... so what help is that? What difference does what something *used* to be make?! If you have a problem *now* it's only a concern what the settings are *now*.

Have you followed through in the debugger to see what's going on?

Tom, That might be another solution. But what I am trying to know is why isn’t this method working. And how is the action **def password=(pass) ** actually being called. Because I didn’t see any exclusive line where it is being called. My analysis says it is being executed because of the attr_accessor and having name same as that of the column password. Am I right? It’s hard to say because it’s extremely hard to piece together what code exactly is excuting - seems like there’s at least 3 different versions in this thread, but it sounds to me like you’ve

definte a password= method always calls self.password= which (by definition) calls your password= method and so you end up in an infinite recursion.

The original tutorial you followed used a different name for the attribute so instead of calling self.password they were calling self.hashed_password= instead. You could either follow the tutorial (although as others have pointed it, it is really old (Rails 3.2has a has_secure_password that handles this) or use self.write_attribute(:password, some_value) when you want to store the hashed value

Fred

Let me make it clear what exactly I have tried.

The original article contains following code,

    def password=(pass)
@password=pass
self.salt = User.random_string(10) if !self.salt?
self.hashed_password = User.encrypt(@password, self.salt)
end

Since I have password as the field in database to save my password, I changed hashed_password to password

def password=(pass) @password1=pass self.salt = SecureRandom.hex(10) if !self.salt? self.password = User.encrypt(@password1, self.salt) end

This gave me the error,

SystemStackError (stack level too deep):

app/models/user.rb:26:in `password=’

app/models/user.rb:27:in `password=’

app/controllers/user_controlle r.rb:31:in `new’

app/controllers/user_controller.rb:31:in `createuser’

So I updated the method as follows,

def passwordtext=(pass) @password1=pass

self.salt = SecureRandom.hex(10) if !self.salt?
self.password = User.encrypt(@password1, self.salt)

end

And added a attr_accessor named as passwordtext. Also I renamed the password_field_tag in the view as “passwordtext”. Doing this didn’t help at all as the passwordtext method didn’t execute.

So, the problem how should I rename my variables to make it work.

Regards Sumit Srivastava

The power of imagination makes us infinite…

So I updated the method as follows,

def passwordtext=(pass) @password1=pass

self.salt = SecureRandom.hex(10) if !self.salt?
self.password = User.encrypt(@password1, self.salt)

end

And added a attr_accessor named as passwordtext. Also I renamed the password_field_tag in the view as “passwordtext”. Doing this didn’t help at all as the passwordtext method didn’t execute.

So, the problem how should I rename my variables to make it work.

Not sure what you mean by ‘didn’t execute’ but the instance variable you set should be @passwordtext if that’s the name you gave attr_accessor. You also want to make sure you passwordtext= method is defined after the call to attr_accessor

Fred

By “didn’t execute”, I mean that when I use “password” as name of this method, it is called some way. But the same doesn’t happens when it is named as “passwordtext”, i.e., it is not even called.

Regards Sumit Srivastava

The power of imagination makes us infinite…