Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Based on the above answers, I have done some experimentation to obtain the desired result. I ended up making a "private" password_hash attribute and a virtual accessor called password. </p> <p>I made some observations in this process:</p> <ul> <li><p>It seems that ActiveRecord doesn't have any concept of private attributes. Making the accessor methods private using symbols such as <code>private :password, :password=</code> is not an option, because Rails throws an <code>NameError: undefined method</code> when instantiating a model, as the model itself does not have these two methods defined (they seem to be inherited from <code>ActiveRecord::Base</code>).</p></li> <li><p>Overriding the password_hash accessors with pure nothing is great for preventing the manipulation of the attribute, however it also means that ActiveRecord itself fails when updating the password_hash attribute as it is calling an empty implementation.</p></li> </ul> <p>So making the accessors private fails because they're undefined in the actual model. Defining them also fails, because it breaks ActiveRecord. So what can you do?</p> <p>I did both and a bit more. I made the accessors private, defined them and implemented both by calling <code>super</code>. This prevents the controller (and the rails console) from accessing them by throwing a <code>NoMethodError</code>, but doesn't reject ActiveRecord.</p> <h3>A side note: Validation issues</h3> <p>One problem I encountered with my approach was broken validation. Enforcing a minimum length on the password_hash was no good as any password (even nothing) results in a 128 character SHA512 hash. So validating the hash made little sense. Instead I added validation to the virtual password accessor and added a <code>before_save :hash_password</code> callback which checks to see if the virtual accessor attribute has been set and if so, hashes it and writes it to the password_hash attribute.</p> <h2>Final implementation</h2> <p>My implementation ended up this way:</p> <pre><code>class User &lt; ActiveRecord::Base attr_accessible :first_name, :last_name, :email attr_accessor :password validates :password, :length =&gt; { :minimum =&gt; 8 }, :if =&gt; :password_changed? validates :first_name, :last_name, :email, presence: true # Various associations before_save :hash_password def password_correct?(p) if(password.present?) password == p else read_attribute(:password_hash) == hash_string(p) end end def role_symbols roles.collect do |r| r.name.to_sym end end private def hash_string(input) Digest::SHA2.new(512).update(input).hexdigest end def hash_password if(password.present?) write_attribute(:password_hash, hash_string(password)) self.password = nil end end def password_changed? password.present? or new_record? end def password_hash super end def password_hash=(p) super end end </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload