Greetings
Today i completed watching and working with Chapter 7 of Learn Rails by Example
This lesson introduced me to learn various concepts such as
- How to authenticate users using password
- Migrations, Validations
- Virtual Attributes
- Private Methods
- Active Record Callback
- Password encryption
The first step is to include the two attributes password, password_confirmation in the User model
These attributes are added using the concept of Virtual Attributes
Virtual attributes doesn’t exist in the database
It is available only in memory.
Virtual attribute is achieved using attr_accessor
Edit the user.rb to include ‘password’ ( virtual ) attribute
attr_accessor :password
password_conformation attribute is automatically added as a virtual attribute by using the following code in user.rb
validates :password, :conformation => true
Then i followed the screencast to write the test cases for the ‘password‘ attributes
All the test cases should be inside spec/models/user_spec.rb
Password Attributes Test Cases
describe "passwords" do
before(:each) do
@user = User.new(@attr)
end
it "should have a password attribute" do
@user.should respond_to(:password)
end
it "should have a password confirmation attribute" do
@user.should respond_to(:password_confirmation)
end
end
Password Validations Test Cases
describe "password validations" do
it "should require a password" do
User.new(@attr.merge(:password => "", :password_confirmation => "")).
should_not be_valid
end
it "should require a matching password confirmation" do
User.new(@attr.merge(:password_confirmation => "invalid")).
should_not be_valid
end
it "should reject short passwords" do
short = "a" * 5
hash = @attr.merge(:password => short, :password_confirmation => short)
User.new(hash).should_not be_valid
end
it "should reject long passwords" do
long = "a" * 41
hash = @attr.merge(:password => long, :password_confirmation => long)
User.new(hash).should_not be_valid
end
end
To store a password which must be encrypted, we need to add a column( encrypted_password ) in the database
To implement it, we need to run the migration command
rails generate migration add_password_to_users encrypted_password:string
Before saving the password, the encrypted password in the database must be compared and equal to the encrypted version of submitted password.
To have a secure password, a hashing algorithm called SHA2 and salt is used
Salt is a combination of current time and user password
Salt is added by running a migration command
rails generate migration add_salt_to_users salt:string
Password Encryption Test Cases
describe "password encryption" do
before(:each) do
@user = User.create!(@attr)
end
it "should have an encrypted password attribute" do
@user.should respond_to(:encrypted_password)
end
it "should set the encrypted password attribute" do
@user.encrypted_password.should_not be_blank
end
it "should have a salt" do
@user.should respond_to(:salt)
end
describe "has_password? method" do
it "should exist" do
@user.should respond_to(:has_password?)
end
it "should return true if the passwords match" do
@user.has_password?(@attr[:password]).should be_true
end
it "should return false if the passwords don't match" do
@user.has_password?("invalid").should be_false
end
end
describe "authenticate method" do
it "should exist" do
User.should respond_to(:authenticate)
end
it "should return nil on email/password mismatch" do
User.authenticate(@attr[:email], "wrongpass").should be_nil
end
it "should return nil for an email address with no user" do
User.authenticate("bar@foo.com", @attr[:password]).should be_nil
end
it "should return the user on email/password match" do
User.authenticate(@attr[:email], @attr[:password]).should == @user
end
end
end
The user.rb file must be modified to looks as below
# == Schema Information
# Schema version: 20110216065316
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime
# updated_at :datetime
# encrypted_password :string(255)
#
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation
email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_save :encrypt_password
def has_password?(submitted_password)
encrypted_password == encrypt(submitted_password)
end
class << self
def authenticate(email, submitted_password)
user = find_by_email(email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
To add an image to our application we need to include a gravatar gem in Gemfile
Edit the Gemfile
gem ‘gravatar_image_tag’, ‘0.1.0’
and run bundle install
Git URL : https://github.com/rajee/sample_app
Heroku URL : http://floating-river-137.heroku.com/users/1
By 3:45 p.m. we had a SCRUM meet and i told as i completed working with Chapter 7
Have to continue my learning with a new lesson ( Chapter 8 ) by tomorrow 🙂 🙂
Read Full Post »