Created
July 13, 2012 09:50
-
-
Save jacortinas/3103980 to your computer and use it in GitHub Desktop.
The difference between testing a small authentication file with BCrypt cost set to normal, then set to min.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/models/user/authentication.rb | |
require 'bcrypt' | |
class User | |
# Password authentication related methods and utilities. | |
module Authentication | |
extend ActiveSupport::Concern | |
module ClassMethods | |
# Authenticate the login credentials of a user. | |
# | |
# @param username_or_email [String] the username or email. | |
# @param password [String] the password. | |
# | |
# @return [User, NilClass] the user if authenticated, otherwise nil. | |
def authenticate(username_or_email, password) | |
user = find_by_username_or_email(username_or_email) | |
user if user && user.correct_password?(password) | |
end | |
# Find a user by username or email, case insensitive. | |
# | |
# @param username_or_email [String] | |
# | |
# @return [User, NilClass] if found, matching user otherwise nil. | |
def find_by_username_or_email(username_or_email) | |
if username_or_email.present? | |
# builds a `LOWER('blah')` arel node. | |
value = arel_table.lower(username_or_email) | |
# build the where statements as lower case comparisons as well. | |
username_query = arel_table[:username].lower.eq(value) | |
email_query = arel_table[:email].lower.eq(value) | |
where(username_query.or(email_query)).first | |
end | |
end | |
end # module ClassMethods | |
# Whether the unencrypted password matches the stored password digest. | |
# | |
# @param unencrypted_password [String] unencrypted plain text password. | |
# | |
# @return [boolean] true if the password is correct, otherwise false. | |
def correct_password?(unencrypted_password) | |
BCrypt::Password.new(password_digest) == unencrypted_password | |
end | |
# @return [String] the user's password. | |
def password; @password; end # avoiding an attr_reader 'cause YARD pukes. | |
# Encrypts an unencrypted password and assigns it the password_digest | |
# attribute. | |
# | |
# @param unencrypted_password [String] unencrypted plain text password. | |
def password=(unencrypted_password) | |
@password = unencrypted_password | |
unless unencrypted_password.blank? | |
self.password_digest = BCrypt::Password.create(unencrypted_password) | |
end | |
end | |
end # module Authentication | |
end # class User |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# spec/models/user/authentication.rb | |
require 'spec_helper' | |
describe User::Authentication do | |
subject { User.new } | |
describe ".authenticate" do | |
it "tries to find the user" do | |
User.should_receive(:find_by_username_or_email) | |
User.authenticate('[email protected]', 'blahblah') | |
end | |
it "checks if the password is correct" do | |
user = double(:user) | |
User.stub(:find_by_username_or_email).and_return user | |
user.should_receive(:correct_password?) | |
User.authenticate('[email protected]', 'blahblah') | |
end | |
it "returns the found user if authenticated" do | |
user = double(:user) | |
User.stub(:find_by_username_or_email).and_return user | |
user.stub(:correct_password?).and_return true | |
User.authenticate('[email protected]', 'blahblah') | |
end | |
it "returns nil if the user doesn't exists" do | |
User.stub(:find_by_username_or_email).and_return nil | |
User.authenticate('[email protected]', 'blahblah') | |
end | |
it "returns nil if the password is incorrect" do | |
user = double(:user) | |
User.stub(:find_by_username_or_email).and_return user | |
user.stub(:correct_password?).and_return false | |
User.authenticate('[email protected]', 'blahblah') | |
end | |
end | |
describe ".find_by_username_or_email" do | |
before :all do | |
@user = FactoryGirl.create :user, email: '[email protected]', username: 'cosmo' | |
end | |
it "finds a user by username case insensitively" do | |
User.find_by_username_or_email('cosmo').should be == @user | |
User.find_by_username_or_email('COSMO').should be == @user | |
end | |
it "finds a user by email case insensitively" do | |
User.find_by_username_or_email('[email protected]').should be == @user | |
User.find_by_username_or_email('[email protected]').should be == @user | |
end | |
it "is nil if nothing is found" do | |
User.find_by_username_or_email('blahblahblah').should be_nil | |
end | |
it "does not query the database if a blank value is passed in" do | |
method = :find_by_username_or_email | |
User.should_not query_the_database.when_calling(method).with('') | |
User.should_not query_the_database.when_calling(method).with(nil) | |
end | |
end | |
describe "#correct_password" do | |
it "is true if the passed in password matches the password digest" do | |
subject.password = 'blah' | |
subject.correct_password?('blah').should be_true | |
end | |
it "false if the password does not match" do | |
subject.password = 'omfg' | |
subject.correct_password?('notthesame').should be_false | |
end | |
end | |
describe "#password=" do | |
it "sets the password attribute" do | |
subject.password = 'blah' | |
subject.password.should be == 'blah' | |
end | |
it "sets the password digest" do | |
subject.should_receive(:password_digest=) | |
subject.password = 'blah' | |
end | |
it "does not set the password digest if the unencrypted password is blank" do | |
subject.should_not_receive(:password_digest=) | |
subject.password = '' | |
subject.password = nil | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# config/initializers/bcrypt.rb | |
require 'bcrypt' | |
# BCrypt was made to be slow, by lowering the cost during testing, we are | |
# giving up security (meh) but *literally* shaving seconds off of the total | |
# time for the test suite to run. | |
if Rails.env.test? || Rails.env.cucumber? | |
silence_warnings do | |
BCrypt::Engine::DEFAULT_COST = BCrypt::Engine::MIN_COST | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# With default cost of 10 passes | |
Finished in 0.66575 seconds |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# With minimum allowed cost of 4 passes | |
Finished in 0.10229 seconds |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment