Password, hashed_password and validation

(Oringinally posted with several other titles with no response…maybe
third time is the charm!)

I’m using a virtual attribute for password in my user model and a
hashed_password field for the persisted hashed value (a la AWDWR).

However, I am having issues using fixtures and unit tests to validate my
models.

Example, I have a User model that has a password validation clause that
looks something like this:

validates_length_of :password, :within => 6…30

And I have a fixture for a test user that looks something like this:

user_jeff:
id: 1
username: jeff
real_name: Jeff Shmeff
salt: <%= SALT %>
hashed_password: <%= User.encrypted_password(‘secret’, SALT) %>
email: [email protected]

if I ever attempt something like this in my User unit tests…

user = users(:user_jeff)
assert user.valid?

I will get an assertion failure like this:
‘Password is too short (minimum is 6 characters)’

temporarily to get around this, I’ve taken to doing something like this
in my test setup method:

def setup
@user = User.find(1)
@user.password = “this is a hack”
end

and using @user for most of my tests instead of users(:user_jeff).
But, that sucks.

Any suggestions as to what I SHOULD be doing?

Jeff

i think you have a password= method in your model and a validation for
that.
instead SALTing and hashing the pass yourself, user passwor: foopasswd
in
your fixture. works.

2006/8/21, J Amiel [email protected]:

real_name: Jeff Shmeff

‘Password is too short (minimum is 6 characters)’
But, that sucks.

Any suggestions as to what I SHOULD be doing?

Jeff


Posted via http://www.ruby-forum.com/.


Michael S. [email protected]

www.siebert-wd.de - Gedanken lesen
www.stellar-legends.de - Weltraum-Browsergame im Alpha-Stadium

I do have a password= method in the model…

def password=(pwd)
@password = pwd
create_new_salt
self.hashed_password = User.encrypted_password(self.password,
self.salt)
end

Butif I attempt to put password: whatever in my fixture, rails throws a
database error while trying to populate a non-existant column in the
table (I guess the fixtures logic doesn’t really refer back to the
activerecord to determine the difference between virtual and ‘real’
attributes)

Michael S. wrote:

i think you have a password= method in your model and a validation for
that.
instead SALTing and hashing the pass yourself, user passwor: foopasswd
in
your fixture. works.

Maybe I should also have mentioned that User also contains:

    def password=(password_)
            @password.password = password_
    end

*** Disclaimer: I am new to rails myself. ***

if I ever attempt something like this in my User unit tests…

user = users(:user_jeff)
assert user.valid?

I will get an assertion failure like this:
‘Password is too short (minimum is 6 characters)’

Any suggestions as to what I SHOULD be doing?

I’m not sure what the “best practice” way of doing it is, but I was
coming up
against the same problem recently myself. I will probably eventually
use one
of the popular user management generators, but I wanted to implement it
myself first, so that I understood properly the issues involved. I
don’t
have time right now to give a detailed explanation of how I handled it,
but
the gist of it is this:

I created a separate Password class that was responsible for handling
all
password functionality. This class could be initialized either via the
hashed password and salt (for when it is initialized from the database),
or
by passing it a plain text password. Rather than doing a
“validates_length_of :password” in the user model, I did
##############
def validate
errorString = @password.validation_errors
if errorString then
errors.add :password, errorString
end
end
##############

where @password is an instance of my Password class. So the Password
class
does the actual validation in validation_errors(). If it is initialized
with
the password hash, it will assume that everything is ok, and return nil
from
validation_errors (the user class does not allow the password_hash or
password_salt to be set by a params hash). If it was initialized from a
plaintext password, it will check that everything is ok, and return an
error
string if anything is wrong. This way, errors are checked when the
password
is first stored, but you won’t get spurious errors when the password
info is
loaded from the database.

I also have (in my User model)

##############
before_save :encrypt_password

def encrypt_password
self.password_hash, self.password_salt =
@password.encrypted_version
end
##############

Hmm, it seems that I have given a reasonably detailed explanation after
all.
Let me know if you need more clarification though (there were a couple
of
other things I had to do… I had to add the following functions to User
as
well; let me know if you need me to explain why):

##############
protected
# This is only called when explicitly creating an object,
# not when it is pulled from the database
def initialize(*params)
@password = Password.new(nil, nil)
super
end

    # This is called regardless of whether the object
    # is explicitly created or not.
    def after_initialize
            @password ||= Password.new(self.password_hash,

self.password_salt)
end
##############

Oh, and if anyone thinks I’ve done something stupid, let me know too…
(see
disclaimer at the beginning of this mail :slight_smile: )

Michael.