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
|
||||
* 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.
|
||||
* 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.
|
||||
* 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
|
||||
* Fix a bug with STI.
|
||||
|
|
1
Rakefile
1
Rakefile
|
@ -46,6 +46,7 @@ begin
|
|||
s.files = FileList["[A-Z]*", "{app,config,lib}/**/*"]
|
||||
s.extra_rdoc_files = FileList["[A-Z]*"] - %w(Gemfile Rakefile)
|
||||
s.add_dependency("warden", "~> 0.10.5")
|
||||
s.add_dependency("bcrypt-ruby", "~> 2.1.2")
|
||||
end
|
||||
|
||||
Jeweler::GemcutterTasks.new
|
||||
|
|
|
@ -97,7 +97,7 @@ module Devise
|
|||
|
||||
# Used to define the password encryption algorithm.
|
||||
mattr_accessor :encryptor
|
||||
@@encryptor = :sha1
|
||||
@@encryptor = nil
|
||||
|
||||
# Store scopes 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
|
||||
# the default behavior.
|
||||
class AuthlogicSha512 < Base
|
||||
|
||||
# Gererates a default password digest based on salt, pepper and the
|
||||
# incoming password.
|
||||
def self.digest(password, stretches, salt, pepper)
|
||||
|
@ -15,7 +14,6 @@ module Devise
|
|||
stretches.times { digest = Digest::SHA512.hexdigest(digest) }
|
||||
digest
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,7 +5,6 @@ module Devise
|
|||
# = BCrypt
|
||||
# Uses the BCrypt hash algorithm to encrypt passwords.
|
||||
class Bcrypt < Base
|
||||
|
||||
# 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.
|
||||
def self.digest(password, stretches, salt, pepper)
|
||||
|
@ -15,7 +14,6 @@ module Devise
|
|||
def self.salt
|
||||
::BCrypt::Engine.generate_salt
|
||||
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 stretches configuration to port the concept of REST_AUTH_DIGEST_STRETCHES
|
||||
class ClearanceSha1 < Base
|
||||
|
||||
# Gererates a default password digest based on salt, pepper and the
|
||||
# incoming password.
|
||||
def self.digest(password, stretches, salt, pepper)
|
||||
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,7 +5,6 @@ module Devise
|
|||
# = Sha1
|
||||
# Uses the Sha1 hash algorithm to encrypt passwords.
|
||||
class Sha1 < Base
|
||||
|
||||
# Gererates a default password digest based on stretches, salt, pepper and the
|
||||
# incoming password.
|
||||
def self.digest(password, stretches, salt, pepper)
|
||||
|
@ -14,14 +13,13 @@ module Devise
|
|||
digest
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
# Generate a SHA1 digest joining args. Generated token is something like
|
||||
# --arg1--arg2--arg3--argN--
|
||||
def self.secure_digest(*tokens)
|
||||
::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
|
||||
end
|
||||
|
||||
# Generate a SHA1 digest joining args. Generated token is something like
|
||||
# --arg1--arg2--arg3--argN--
|
||||
def self.secure_digest(*tokens)
|
||||
::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,7 +5,6 @@ module Devise
|
|||
# = Sha512
|
||||
# Uses the Sha512 hash algorithm to encrypt passwords.
|
||||
class Sha512 < Base
|
||||
|
||||
# Gererates a default password digest based on salt, pepper and the
|
||||
# incoming password.
|
||||
def self.digest(password, stretches, salt, pepper)
|
||||
|
@ -14,14 +13,13 @@ module Devise
|
|||
digest
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
# Generate a Sha512 digest joining args. Generated token is something like
|
||||
# --arg1--arg2--arg3--argN--
|
||||
def self.secure_digest(*tokens)
|
||||
::Digest::SHA512.hexdigest('--' << tokens.flatten.join('--') << '--')
|
||||
end
|
||||
|
||||
# Generate a Sha512 digest joining args. Generated token is something like
|
||||
# --arg1--arg2--arg3--argN--
|
||||
def self.secure_digest(*tokens)
|
||||
::Digest::SHA512.hexdigest('--' << tokens.flatten.join('--') << '--')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -16,24 +16,29 @@ module Devise
|
|||
# Force routes to be loaded if we are doing any eager load.
|
||||
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
|
||||
flash = [:unauthenticated, :unconfirmed, :invalid, :invalid_token, :timeout, :inactive, :locked]
|
||||
|
||||
translations = begin
|
||||
I18n.available_locales
|
||||
I18n.backend.send(:translations)
|
||||
I18n.t("devise.sessions", :raise => true).keys
|
||||
rescue Exception => e # Do not care if something fails
|
||||
{}
|
||||
end
|
||||
|
||||
translations.each do |locale, translations|
|
||||
keys = flash & (translations[:devise][:sessions].keys) rescue []
|
||||
keys = flash & translations
|
||||
|
||||
if keys.any?
|
||||
ActiveSupport::Deprecation.warn "The following I18n messages in 'devise.sessions' " <<
|
||||
"for locale '#{locale}' are deprecated: #{keys.to_sentence}. Please move them to " <<
|
||||
"'devise.failure' instead."
|
||||
end
|
||||
if keys.any?
|
||||
ActiveSupport::Deprecation.warn "The following I18n messages in 'devise.sessions' " \
|
||||
"are deprecated: #{keys.to_sentence}. Please move them to 'devise.failure' instead."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require 'active_support/secure_random'
|
||||
|
||||
class DeviseInstallGenerator < Rails::Generators::Base
|
||||
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"
|
||||
|
||||
# ==> Configuration for :database_authenticatable
|
||||
# Invoke `rake secret` and use the printed value to setup a pepper to generate
|
||||
# the encrypted password. By default no pepper is used.
|
||||
# config.pepper = "rake secret output"
|
||||
# For bcrypt, this is the cost for hashing the password and defaults to 10. If
|
||||
# using other encryptors, it sets how many times you want the password re-encrypted.
|
||||
config.stretches = 10
|
||||
|
||||
# Configure how many times you want the password re-encrypted. Default is 10.
|
||||
# config.stretches = 10
|
||||
|
||||
# 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
|
||||
# Define which will be the encryption algorithm. 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)
|
||||
# config.encryptor = :sha1
|
||||
config.encryptor = :bcrypt
|
||||
|
||||
# Setup a pepper to generate the encrypted password.
|
||||
config.pepper = <%= ActiveSupport::SecureRandom.hex(64).inspect %>
|
||||
|
||||
# ==> Configuration for :confirmable
|
||||
# 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
|
||||
|
||||
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)
|
||||
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
|
||||
user = new_user
|
||||
assert user.respond_to?(:password)
|
||||
|
@ -28,8 +40,10 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
test 'should generate a base64 hash using SecureRandom for password salt' do
|
||||
ActiveSupport::SecureRandom.expects(:base64).with(15).returns('friendly_token')
|
||||
assert_equal 'friendly_token', new_user.password_salt
|
||||
swap_with_encryptor User, :sha1 do
|
||||
ActiveSupport::SecureRandom.expects(:base64).with(15).returns('friendly_token')
|
||||
assert_equal 'friendly_token', new_user.password_salt
|
||||
end
|
||||
end
|
||||
|
||||
test 'should not generate salt if password is blank' do
|
||||
|
@ -71,24 +85,10 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
|
|||
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
|
||||
User.instance_variable_set(:@encryptor_class, nil)
|
||||
|
||||
swap Devise, :encryptor => :sha512 do
|
||||
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
|
||||
swap_with_encryptor User, :sha512 do
|
||||
user = create_user
|
||||
assert_equal user.encrypted_password, encrypt_password(user, User.pepper, User.stretches, ::Devise::Encryptors::Sha512)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue