Make bcrypt the default encryptor and automatically add a pepper on generation.
This commit is contained in:
parent
bece09c653
commit
0333caeb92
|
@ -1,4 +1,19 @@
|
||||||
== 1.1.rc1
|
== 1.1.0 (to be released)
|
||||||
|
|
||||||
|
* enhancements
|
||||||
|
* Allow to set cookie domain for the remember token. (by github.com/mantas)
|
||||||
|
* Added navigational formats to specify when it should return a 302 and when a 401.
|
||||||
|
* Added authenticate(scope) support in routes (by github.com/wildchild)
|
||||||
|
* Added after_update_path_for to registrations controller (by github.com/thedelchop)
|
||||||
|
* Added anybody_signed_in? helper (by github.com/SSDany)
|
||||||
|
|
||||||
|
* bug fix
|
||||||
|
* confirmation_required? is properly honored on active? calls. (by github.com/paulrosania)
|
||||||
|
|
||||||
|
* deprecations
|
||||||
|
* bcrypt is now the default encryptor
|
||||||
|
|
||||||
|
== 1.1.rc
|
||||||
|
|
||||||
* enhancements
|
* enhancements
|
||||||
* Rails 3 compatibility.
|
* Rails 3 compatibility.
|
||||||
|
@ -17,10 +32,6 @@
|
||||||
* Allow :unlock_strategy to be :none and add :lock_strategy which can be :failed_attempts or none. Setting those values to :none means that you want to handle lock and unlocking by yourself.
|
* Allow :unlock_strategy to be :none and add :lock_strategy which can be :failed_attempts or none. Setting those values to :none means that you want to handle lock and unlocking by yourself.
|
||||||
* No need to append ?unauthenticated=true in URLs anymore since Flash was moved to a middleware in Rails 3.
|
* No need to append ?unauthenticated=true in URLs anymore since Flash was moved to a middleware in Rails 3.
|
||||||
* :activatable is included by default in your models.
|
* :activatable is included by default in your models.
|
||||||
* Allow to set cookie domain for the remember token. (by github.com/mantas)
|
|
||||||
* Added navigational formats to specify when it should return a 302 and when a 401.
|
|
||||||
* Added authenticate(scope) support in routes (by github.com/wildchild)
|
|
||||||
* Added after_update_path_for to registrations controller (by github.com/thedelchop)
|
|
||||||
|
|
||||||
* bug fix
|
* bug fix
|
||||||
* Fix a bug with STI.
|
* Fix a bug with STI.
|
||||||
|
|
1
Rakefile
1
Rakefile
|
@ -46,6 +46,7 @@ begin
|
||||||
s.files = FileList["[A-Z]*", "{app,config,lib}/**/*"]
|
s.files = FileList["[A-Z]*", "{app,config,lib}/**/*"]
|
||||||
s.extra_rdoc_files = FileList["[A-Z]*"] - %w(Gemfile Rakefile)
|
s.extra_rdoc_files = FileList["[A-Z]*"] - %w(Gemfile Rakefile)
|
||||||
s.add_dependency("warden", "~> 0.10.5")
|
s.add_dependency("warden", "~> 0.10.5")
|
||||||
|
s.add_dependency("bcrypt-ruby", "~> 2.1.2")
|
||||||
end
|
end
|
||||||
|
|
||||||
Jeweler::GemcutterTasks.new
|
Jeweler::GemcutterTasks.new
|
||||||
|
|
|
@ -97,7 +97,7 @@ module Devise
|
||||||
|
|
||||||
# Used to define the password encryption algorithm.
|
# Used to define the password encryption algorithm.
|
||||||
mattr_accessor :encryptor
|
mattr_accessor :encryptor
|
||||||
@@encryptor = :sha1
|
@@encryptor = nil
|
||||||
|
|
||||||
# Store scopes mappings.
|
# Store scopes mappings.
|
||||||
mattr_accessor :mappings
|
mattr_accessor :mappings
|
||||||
|
|
|
@ -7,7 +7,6 @@ module Devise
|
||||||
# Warning: it uses Devise's stretches configuration to port Authlogic's one. Should be set to 20 in the initializer to silumate
|
# Warning: it uses Devise's stretches configuration to port Authlogic's one. Should be set to 20 in the initializer to silumate
|
||||||
# the default behavior.
|
# the default behavior.
|
||||||
class AuthlogicSha512 < Base
|
class AuthlogicSha512 < Base
|
||||||
|
|
||||||
# Gererates a default password digest based on salt, pepper and the
|
# Gererates a default password digest based on salt, pepper and the
|
||||||
# incoming password.
|
# incoming password.
|
||||||
def self.digest(password, stretches, salt, pepper)
|
def self.digest(password, stretches, salt, pepper)
|
||||||
|
@ -15,7 +14,6 @@ module Devise
|
||||||
stretches.times { digest = Digest::SHA512.hexdigest(digest) }
|
stretches.times { digest = Digest::SHA512.hexdigest(digest) }
|
||||||
digest
|
digest
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -5,7 +5,6 @@ module Devise
|
||||||
# = BCrypt
|
# = BCrypt
|
||||||
# Uses the BCrypt hash algorithm to encrypt passwords.
|
# Uses the BCrypt hash algorithm to encrypt passwords.
|
||||||
class Bcrypt < Base
|
class Bcrypt < Base
|
||||||
|
|
||||||
# Gererates a default password digest based on stretches, salt, pepper and the
|
# Gererates a default password digest based on stretches, salt, pepper and the
|
||||||
# incoming password. We don't strech it ourselves since BCrypt does so internally.
|
# incoming password. We don't strech it ourselves since BCrypt does so internally.
|
||||||
def self.digest(password, stretches, salt, pepper)
|
def self.digest(password, stretches, salt, pepper)
|
||||||
|
@ -15,7 +14,6 @@ module Devise
|
||||||
def self.salt
|
def self.salt
|
||||||
::BCrypt::Engine.generate_salt
|
::BCrypt::Engine.generate_salt
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,13 +7,11 @@ module Devise
|
||||||
# Warning: it uses Devise's pepper to port the concept of REST_AUTH_SITE_KEY
|
# Warning: it uses Devise's pepper to port the concept of REST_AUTH_SITE_KEY
|
||||||
# Warning: it uses Devise's stretches configuration to port the concept of REST_AUTH_DIGEST_STRETCHES
|
# Warning: it uses Devise's stretches configuration to port the concept of REST_AUTH_DIGEST_STRETCHES
|
||||||
class ClearanceSha1 < Base
|
class ClearanceSha1 < Base
|
||||||
|
|
||||||
# Gererates a default password digest based on salt, pepper and the
|
# Gererates a default password digest based on salt, pepper and the
|
||||||
# incoming password.
|
# incoming password.
|
||||||
def self.digest(password, stretches, salt, pepper)
|
def self.digest(password, stretches, salt, pepper)
|
||||||
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
|
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -5,7 +5,6 @@ module Devise
|
||||||
# = Sha1
|
# = Sha1
|
||||||
# Uses the Sha1 hash algorithm to encrypt passwords.
|
# Uses the Sha1 hash algorithm to encrypt passwords.
|
||||||
class Sha1 < Base
|
class Sha1 < Base
|
||||||
|
|
||||||
# Gererates a default password digest based on stretches, salt, pepper and the
|
# Gererates a default password digest based on stretches, salt, pepper and the
|
||||||
# incoming password.
|
# incoming password.
|
||||||
def self.digest(password, stretches, salt, pepper)
|
def self.digest(password, stretches, salt, pepper)
|
||||||
|
@ -14,14 +13,13 @@ module Devise
|
||||||
digest
|
digest
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Generate a SHA1 digest joining args. Generated token is something like
|
# Generate a SHA1 digest joining args. Generated token is something like
|
||||||
# --arg1--arg2--arg3--argN--
|
# --arg1--arg2--arg3--argN--
|
||||||
def self.secure_digest(*tokens)
|
def self.secure_digest(*tokens)
|
||||||
::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
|
::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -5,7 +5,6 @@ module Devise
|
||||||
# = Sha512
|
# = Sha512
|
||||||
# Uses the Sha512 hash algorithm to encrypt passwords.
|
# Uses the Sha512 hash algorithm to encrypt passwords.
|
||||||
class Sha512 < Base
|
class Sha512 < Base
|
||||||
|
|
||||||
# Gererates a default password digest based on salt, pepper and the
|
# Gererates a default password digest based on salt, pepper and the
|
||||||
# incoming password.
|
# incoming password.
|
||||||
def self.digest(password, stretches, salt, pepper)
|
def self.digest(password, stretches, salt, pepper)
|
||||||
|
@ -14,14 +13,13 @@ module Devise
|
||||||
digest
|
digest
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Generate a Sha512 digest joining args. Generated token is something like
|
# Generate a Sha512 digest joining args. Generated token is something like
|
||||||
# --arg1--arg2--arg3--argN--
|
# --arg1--arg2--arg3--argN--
|
||||||
def self.secure_digest(*tokens)
|
def self.secure_digest(*tokens)
|
||||||
::Digest::SHA512.hexdigest('--' << tokens.flatten.join('--') << '--')
|
::Digest::SHA512.hexdigest('--' << tokens.flatten.join('--') << '--')
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -16,24 +16,29 @@ module Devise
|
||||||
# Force routes to be loaded if we are doing any eager load.
|
# Force routes to be loaded if we are doing any eager load.
|
||||||
config.before_eager_load { |app| app.reload_routes! }
|
config.before_eager_load { |app| app.reload_routes! }
|
||||||
|
|
||||||
|
config.after_initialize do
|
||||||
|
Devise.encryptor ||= begin
|
||||||
|
warn "[WARNING] config.encryptor is not set in your config/initializers/devise.rb. " \
|
||||||
|
"Devise will then set it to :bcrypt. If you were using the previous default " \
|
||||||
|
"encryptor, please add config.encryptor = :sha1 to your configuration file."
|
||||||
|
:bcrypt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
config.after_initialize do
|
config.after_initialize do
|
||||||
flash = [:unauthenticated, :unconfirmed, :invalid, :invalid_token, :timeout, :inactive, :locked]
|
flash = [:unauthenticated, :unconfirmed, :invalid, :invalid_token, :timeout, :inactive, :locked]
|
||||||
|
|
||||||
translations = begin
|
translations = begin
|
||||||
I18n.available_locales
|
I18n.t("devise.sessions", :raise => true).keys
|
||||||
I18n.backend.send(:translations)
|
|
||||||
rescue Exception => e # Do not care if something fails
|
rescue Exception => e # Do not care if something fails
|
||||||
{}
|
{}
|
||||||
end
|
end
|
||||||
|
|
||||||
translations.each do |locale, translations|
|
keys = flash & translations
|
||||||
keys = flash & (translations[:devise][:sessions].keys) rescue []
|
|
||||||
|
|
||||||
if keys.any?
|
if keys.any?
|
||||||
ActiveSupport::Deprecation.warn "The following I18n messages in 'devise.sessions' " <<
|
ActiveSupport::Deprecation.warn "The following I18n messages in 'devise.sessions' " \
|
||||||
"for locale '#{locale}' are deprecated: #{keys.to_sentence}. Please move them to " <<
|
"are deprecated: #{keys.to_sentence}. Please move them to 'devise.failure' instead."
|
||||||
"'devise.failure' instead."
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
require 'active_support/secure_random'
|
||||||
|
|
||||||
class DeviseInstallGenerator < Rails::Generators::Base
|
class DeviseInstallGenerator < Rails::Generators::Base
|
||||||
desc "Creates a Devise initializer and copy locale files to your application."
|
desc "Creates a Devise initializer and copy locale files to your application."
|
||||||
|
|
||||||
|
|
|
@ -27,19 +27,18 @@ Devise.setup do |config|
|
||||||
# config.http_authentication_realm = "Application"
|
# config.http_authentication_realm = "Application"
|
||||||
|
|
||||||
# ==> Configuration for :database_authenticatable
|
# ==> Configuration for :database_authenticatable
|
||||||
# Invoke `rake secret` and use the printed value to setup a pepper to generate
|
# For bcrypt, this is the cost for hashing the password and defaults to 10. If
|
||||||
# the encrypted password. By default no pepper is used.
|
# using other encryptors, it sets how many times you want the password re-encrypted.
|
||||||
# config.pepper = "rake secret output"
|
config.stretches = 10
|
||||||
|
|
||||||
# Configure how many times you want the password re-encrypted. Default is 10.
|
# Define which will be the encryption algorithm. Devise also supports encryptors
|
||||||
# config.stretches = 10
|
# from others authentication tools as :clearance_sha1, :authlogic_sha512 (then
|
||||||
|
# you should set stretches above to 20 for default behavior) and :restful_authentication_sha1
|
||||||
# Define which will be the encryption algorithm. Supported algorithms are :sha1
|
|
||||||
# (default), :sha512 and :bcrypt. Devise also supports encryptors from others
|
|
||||||
# authentication tools as :clearance_sha1, :authlogic_sha512 (then you should set
|
|
||||||
# stretches above to 20 for default behavior) and :restful_authentication_sha1
|
|
||||||
# (then you should set stretches to 10, and copy REST_AUTH_SITE_KEY to pepper)
|
# (then you should set stretches to 10, and copy REST_AUTH_SITE_KEY to pepper)
|
||||||
# config.encryptor = :sha1
|
config.encryptor = :bcrypt
|
||||||
|
|
||||||
|
# Setup a pepper to generate the encrypted password.
|
||||||
|
config.pepper = <%= ActiveSupport::SecureRandom.hex(64).inspect %>
|
||||||
|
|
||||||
# ==> Configuration for :confirmable
|
# ==> Configuration for :confirmable
|
||||||
# The time you want to give your user to confirm his account. During this time
|
# The time you want to give your user to confirm his account. During this time
|
||||||
|
|
|
@ -3,10 +3,22 @@ require 'digest/sha1'
|
||||||
|
|
||||||
class DatabaseAuthenticatableTest < ActiveSupport::TestCase
|
class DatabaseAuthenticatableTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
def encrypt_password(user, pepper=User.pepper, stretches=User.stretches, encryptor=::Devise::Encryptors::Sha1)
|
def encrypt_password(user, pepper=User.pepper, stretches=User.stretches, encryptor=User.encryptor_class)
|
||||||
encryptor.digest('123456', stretches, user.password_salt, pepper)
|
encryptor.digest('123456', stretches, user.password_salt, pepper)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def swap_with_encryptor(klass, encryptor, options={})
|
||||||
|
klass.instance_variable_set(:@encryptor_class, nil)
|
||||||
|
|
||||||
|
swap klass, options.merge(:encryptor => encryptor) do
|
||||||
|
begin
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
klass.instance_variable_set(:@encryptor_class, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test 'should respond to password and password confirmation' do
|
test 'should respond to password and password confirmation' do
|
||||||
user = new_user
|
user = new_user
|
||||||
assert user.respond_to?(:password)
|
assert user.respond_to?(:password)
|
||||||
|
@ -28,8 +40,10 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'should generate a base64 hash using SecureRandom for password salt' do
|
test 'should generate a base64 hash using SecureRandom for password salt' do
|
||||||
ActiveSupport::SecureRandom.expects(:base64).with(15).returns('friendly_token')
|
swap_with_encryptor User, :sha1 do
|
||||||
assert_equal 'friendly_token', new_user.password_salt
|
ActiveSupport::SecureRandom.expects(:base64).with(15).returns('friendly_token')
|
||||||
|
assert_equal 'friendly_token', new_user.password_salt
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'should not generate salt if password is blank' do
|
test 'should not generate salt if password is blank' do
|
||||||
|
@ -71,24 +85,10 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'should fallback to devise stretches default configuration' do
|
|
||||||
swap Devise, :stretches => 1 do
|
|
||||||
user = new_user
|
|
||||||
assert_equal encrypt_password(user, nil, 1), user.encrypted_password
|
|
||||||
assert_not_equal encrypt_password(user, nil, 2), user.encrypted_password
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'should respect encryptor configuration' do
|
test 'should respect encryptor configuration' do
|
||||||
User.instance_variable_set(:@encryptor_class, nil)
|
swap_with_encryptor User, :sha512 do
|
||||||
|
user = create_user
|
||||||
swap Devise, :encryptor => :sha512 do
|
assert_equal user.encrypted_password, encrypt_password(user, User.pepper, User.stretches, ::Devise::Encryptors::Sha512)
|
||||||
begin
|
|
||||||
user = create_user
|
|
||||||
assert_equal user.encrypted_password, encrypt_password(user, User.pepper, User.stretches, ::Devise::Encryptors::Sha512)
|
|
||||||
ensure
|
|
||||||
User.instance_variable_set(:@encryptor_class, nil)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue