Skip to content
This repository has been archived by the owner on Jul 28, 2021. It is now read-only.

Add support for per-user password salt with bcrypt #12

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.9.3-p194
ruby-2.1.2
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ To use the ActiveRecord authenticator, configure it in your cas.yml:
table: "users"
username_column: "username"
password_column: "password"
pepper: "suffix of the password" # optional
password_salt_column: "password_salt" # optional; for per-user password suffix
pepper: "suffix of the password" # optional; for shared password suffix
extra_attributes:
email: "email_database_column"
fullname: "displayname_database_column"
Expand Down
13 changes: 7 additions & 6 deletions lib/casino/activerecord_authenticator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ def validate(username, password)
@model.verify_active_connections!
user = @model.send("find_by_#{@options[:username_column]}!", username)
password_from_database = user.send(@options[:password_column])
salt_from_database = @options[:password_salt_column].blank? ? nil : user.send(@options[:password_salt_column])

if valid_password?(password, password_from_database)
if valid_password?(password, password_from_database, salt_from_database)
{ username: user.send(@options[:username_column]), extra_attributes: extra_attributes(user) }
else
false
Expand All @@ -39,22 +40,22 @@ def validate(username, password)
end

private
def valid_password?(password, password_from_database)
def valid_password?(password, password_from_database, salt_from_database)
return false if password_from_database.blank?
magic = password_from_database.split('$')[1]
case magic
when /\A2a?\z/
valid_password_with_bcrypt?(password, password_from_database)
valid_password_with_bcrypt?(password, password_from_database, salt_from_database)
when /\AH\z/, /\AP\z/
valid_password_with_phpass?(password, password_from_database)
else
valid_password_with_unix_crypt?(password, password_from_database)
end
end

def valid_password_with_bcrypt?(password, password_from_database)
password_with_pepper = password + @options[:pepper].to_s
BCrypt::Password.new(password_from_database) == password_with_pepper
def valid_password_with_bcrypt?(password, password_from_database, salt_from_database)
password_with_salt_and_pepper = password + salt_from_database.to_s + @options[:pepper].to_s
BCrypt::Password.new(password_from_database) == password_with_salt_and_pepper
end

def valid_password_with_unix_crypt?(password, password_from_database)
Expand Down
36 changes: 36 additions & 0 deletions spec/casino_core/activerecord_authenticator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

describe CASino::ActiveRecordAuthenticator do

let(:salt_column) { nil }
let(:pepper) { nil }
let(:extra_attributes) {{ email: 'mail_address' }}
let(:options) do
Expand All @@ -14,6 +15,7 @@
table: 'users',
username_column: 'username',
password_column: 'password',
password_salt_column: salt_column,
pepper: pepper,
extra_attributes: extra_attributes
}
Expand All @@ -31,6 +33,7 @@
create_table :users do |t|
t.string :username
t.string :password
t.string :salt
t.string :mail_address
end
end
Expand Down Expand Up @@ -132,6 +135,39 @@
end
end

context "support for bcrypt with salt" do
let(:salt_column) { 'salt' }

before do
described_class::User.create!(
username: "test3.1",
password: "$2a$10$ehbsWmnBn2/Js1qbksqUj.HwuBurFPTsJIyjIbLDqiTwa3281VK1y", # password: testpassword3.1
salt: "deadbeef",
mail_address: "[email protected]")
end

it "is able to handle bcrypt password hashes with salt" do
subject.validate("test3.1", "testpassword3.1").should be_instance_of(Hash)
end
end

context "support for bcrypt with salt and pepper" do
let(:salt_column) { 'salt' }
let(:pepper) { 'abcdefg' }

before do
described_class::User.create!(
username: "test3.2",
password: "$2a$10$1T0wvdfIdPm4DmtY4imLWO1BqRMNH9uXiC747ukE1TnN9pKB5Q/9e", # password: testpassword3.2
salt: "deadbeef",
mail_address: "[email protected]")
end

it "is able to handle bcrypt password hashes with salt and pepper" do
subject.validate("test3.2", "testpassword3.2").should be_instance_of(Hash)
end
end

context 'support for phpass' do
before do
described_class::User.create!(
Expand Down