diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc index 7cd845f9..5eb95302 100644 --- a/CHANGELOG.rdoc +++ b/CHANGELOG.rdoc @@ -2,6 +2,7 @@ * enhancements * Devise model generator now works with engines + * Devise encryptable was moved to its new gem (http://github.com/plataformatec/devise-encryptable) * deprecations * Deprecations warnings added on Devise 2.0 are now removed with their features diff --git a/lib/devise.rb b/lib/devise.rb index f4aec231..1f5a1629 100644 --- a/lib/devise.rb +++ b/lib/devise.rb @@ -20,15 +20,6 @@ module Devise autoload :UrlHelpers, 'devise/controllers/url_helpers' end - module Encryptors - autoload :Base, 'devise/encryptors/base' - autoload :AuthlogicSha512, 'devise/encryptors/authlogic_sha512' - autoload :ClearanceSha1, 'devise/encryptors/clearance_sha1' - autoload :RestfulAuthenticationSha1, 'devise/encryptors/restful_authentication_sha1' - autoload :Sha512, 'devise/encryptors/sha512' - autoload :Sha1, 'devise/encryptors/sha1' - end - module Mailers autoload :Helpers, 'devise/mailers/helpers' end @@ -52,15 +43,6 @@ module Devise # True values used to check params TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'] - # Declare encryptors length which are used in migrations. - ENCRYPTORS_LENGTH = { - :sha1 => 40, - :sha512 => 128, - :clearance_sha1 => 40, - :restful_authentication_sha1 => 40, - :authlogic_sha512 => 128 - } - # Custom domain for cookies. Not set by default mattr_accessor :rememberable_options @@rememberable_options = {} @@ -144,10 +126,6 @@ module Devise mattr_accessor :pepper @@pepper = nil - # Used to define the password encryption algorithm. - mattr_accessor :encryptor - @@encryptor = nil - # Scoped views. Since it relies on fallbacks to render default views, it's # turned off by default. mattr_accessor :scoped_views @@ -223,7 +201,9 @@ module Devise mattr_accessor :router_name @@router_name = nil - # DEPRECATION + def self.encryptor=(value) + warn "\n[DEVISE] To select a encryption which isn't bcrypt, you should use devise-encryptable gem.\n" + end def self.use_salt_as_remember_token=(value) warn "\n[DEVISE] Devise.use_salt_as_remember_token is deprecated and has no effect. Please remove it.\n" diff --git a/lib/devise/encryptors/authlogic_sha512.rb b/lib/devise/encryptors/authlogic_sha512.rb deleted file mode 100644 index 9ff327ed..00000000 --- a/lib/devise/encryptors/authlogic_sha512.rb +++ /dev/null @@ -1,19 +0,0 @@ -require "digest/sha2" - -module Devise - module Encryptors - # = AuthlogicSha512 - # Simulates Authlogic's default encryption mechanism. - # Warning: it uses Devise's stretches configuration to port Authlogic's one. Should be set to 20 in the initializer to simulate - # the default behavior. - class AuthlogicSha512 < Base - # Generates a default password digest based on salt, pepper and the - # incoming password. - def self.digest(password, stretches, salt, pepper) - digest = [password, salt].flatten.join('') - stretches.times { digest = Digest::SHA512.hexdigest(digest) } - digest - end - end - end -end \ No newline at end of file diff --git a/lib/devise/encryptors/base.rb b/lib/devise/encryptors/base.rb deleted file mode 100644 index c5085469..00000000 --- a/lib/devise/encryptors/base.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Devise - # Implements a way of adding different encryptions. - # The class should implement a self.digest method that taks the following params: - # - password - # - stretches: the number of times the encryption will be applied - # - salt: the password salt as defined by devise - # - pepper: Devise config option - # - module Encryptors - class Base - def self.digest - raise NotImplemented - end - - def self.salt(stretches) - Devise.friendly_token[0,20] - end - - def self.compare(encrypted_password, password, stretches, salt, pepper) - Devise.secure_compare(encrypted_password, digest(password, stretches, salt, pepper)) - end - end - end -end diff --git a/lib/devise/encryptors/clearance_sha1.rb b/lib/devise/encryptors/clearance_sha1.rb deleted file mode 100644 index 2857eb6b..00000000 --- a/lib/devise/encryptors/clearance_sha1.rb +++ /dev/null @@ -1,17 +0,0 @@ -require "digest/sha1" - -module Devise - module Encryptors - # = ClearanceSha1 - # Simulates Clearance's default encryption mechanism. - # 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 - # Generates 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 \ No newline at end of file diff --git a/lib/devise/encryptors/restful_authentication_sha1.rb b/lib/devise/encryptors/restful_authentication_sha1.rb deleted file mode 100644 index 1f27985c..00000000 --- a/lib/devise/encryptors/restful_authentication_sha1.rb +++ /dev/null @@ -1,22 +0,0 @@ -require "digest/sha1" - -module Devise - module Encryptors - # = RestfulAuthenticationSha1 - # Simulates Restful Authentication's default encryption mechanism. - # 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. Should be set to 10 in - # the initializer to simulate the default behavior. - class RestfulAuthenticationSha1 < Base - - # Generates a default password digest based on salt, pepper and the - # incoming password. - def self.digest(password, stretches, salt, pepper) - digest = pepper - stretches.times { digest = Digest::SHA1.hexdigest([digest, salt, password, pepper].flatten.join('--')) } - digest - end - - end - end -end diff --git a/lib/devise/encryptors/sha1.rb b/lib/devise/encryptors/sha1.rb deleted file mode 100644 index f10b3723..00000000 --- a/lib/devise/encryptors/sha1.rb +++ /dev/null @@ -1,25 +0,0 @@ -require "digest/sha1" - -module Devise - module Encryptors - # = Sha1 - # Uses the Sha1 hash algorithm to encrypt passwords. - class Sha1 < Base - # Generates a default password digest based on stretches, salt, pepper and the - # incoming password. - def self.digest(password, stretches, salt, pepper) - digest = pepper - stretches.times { digest = self.secure_digest(salt, digest, password, pepper) } - digest - end - - 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 - end - end -end \ No newline at end of file diff --git a/lib/devise/encryptors/sha512.rb b/lib/devise/encryptors/sha512.rb deleted file mode 100644 index 906a6fa1..00000000 --- a/lib/devise/encryptors/sha512.rb +++ /dev/null @@ -1,25 +0,0 @@ -require "digest/sha2" - -module Devise - module Encryptors - # = Sha512 - # Uses the Sha512 hash algorithm to encrypt passwords. - class Sha512 < Base - # Generates a default password digest based on salt, pepper and the - # incoming password. - def self.digest(password, stretches, salt, pepper) - digest = pepper - stretches.times { digest = self.secure_digest(salt, digest, password, pepper) } - digest - end - - 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 - end - end -end \ No newline at end of file diff --git a/lib/devise/models.rb b/lib/devise/models.rb index 222cffe2..635c378d 100644 --- a/lib/devise/models.rb +++ b/lib/devise/models.rb @@ -89,6 +89,10 @@ module Devise devise_modules_hook! do include Devise::Models::Authenticatable selected_modules.each do |m| + if m == :encryptable && !(defined?(Devise::Models::Encryptable)) + warn "[DEVISE] You're trying to include :encryptable in your model but it is not bundled with the Devise gem anymore. Please add `devise-encryptable` to your Gemfile to proceed.\n" + end + mod = Devise::Models.const_get(m.to_s.classify) if mod.const_defined?("ClassMethods") diff --git a/lib/devise/models/encryptable.rb b/lib/devise/models/encryptable.rb deleted file mode 100644 index 0a94d237..00000000 --- a/lib/devise/models/encryptable.rb +++ /dev/null @@ -1,86 +0,0 @@ -require 'devise/strategies/database_authenticatable' - -module Devise - module Models - # Encryptable module adds support to several encryptors wrapping - # them in a salt and pepper mechanism to increase security. - # - # == Options - # - # Encryptable adds the following options to devise_for: - # - # * +pepper+: a random string used to provide a more secure hash. - # - # * +encryptor+: the encryptor going to be used. By default is nil. - # - # == Examples - # - # User.find(1).valid_password?('password123') # returns true/false - # - module Encryptable - extend ActiveSupport::Concern - - included do - attr_reader :password, :current_password - attr_accessor :password_confirmation - end - - def self.required_fields(klass) - [:password_salt] - end - - # Generates password salt when setting the password. - def password=(new_password) - self.password_salt = self.class.password_salt if new_password.present? - super - end - - # Validates the password considering the salt. - def valid_password?(password) - return false if encrypted_password.blank? - encryptor_class.compare(encrypted_password, password, self.class.stretches, authenticatable_salt, self.class.pepper) - end - - # Overrides authenticatable salt to use the new password_salt - # column. authenticatable_salt is used by `valid_password?` - # and by other modules whenever there is a need for a random - # token based on the user password. - def authenticatable_salt - self.password_salt - end - - protected - - # Digests the password using the configured encryptor. - def password_digest(password) - if password_salt.present? - encryptor_class.digest(password, self.class.stretches, authenticatable_salt, self.class.pepper) - end - end - - def encryptor_class - self.class.encryptor_class - end - - module ClassMethods - Devise::Models.config(self, :encryptor) - - # Returns the class for the configured encryptor. - def encryptor_class - @encryptor_class ||= case encryptor - when :bcrypt - raise "In order to use bcrypt as encryptor, simply remove :encryptable from your devise model" - when nil - raise "You need to give an :encryptor as option in order to use :encryptable" - else - Devise::Encryptors.const_get(encryptor.to_s.classify) - end - end - - def password_salt - self.encryptor_class.salt(self.stretches) - end - end - end - end -end diff --git a/lib/devise/modules.rb b/lib/devise/modules.rb index b59f69e9..1c620ca3 100644 --- a/lib/devise/modules.rb +++ b/lib/devise/modules.rb @@ -10,7 +10,6 @@ Devise.with_options :model => true do |d| end # Other authentications - d.add_module :encryptable d.add_module :omniauthable, :controller => :omniauth_callbacks, :route => :omniauth_callback # Misc after diff --git a/lib/generators/active_record/devise_generator.rb b/lib/generators/active_record/devise_generator.rb index 8aa912ed..aa9c4b96 100644 --- a/lib/generators/active_record/devise_generator.rb +++ b/lib/generators/active_record/devise_generator.rb @@ -55,9 +55,6 @@ CONTENT t.string :current_sign_in_ip t.string :last_sign_in_ip - ## Encryptable - # t.string :password_salt - ## Confirmable # t.string :confirmation_token # t.datetime :confirmed_at diff --git a/lib/generators/devise/orm_helpers.rb b/lib/generators/devise/orm_helpers.rb index 084531ad..97c23887 100644 --- a/lib/generators/devise/orm_helpers.rb +++ b/lib/generators/devise/orm_helpers.rb @@ -4,7 +4,7 @@ module Devise def model_contents <<-CONTENT # Include default devise modules. Others available are: - # :token_authenticatable, :encryptable, :confirmable, + # :token_authenticatable, :confirmable, # :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable diff --git a/lib/generators/mongoid/devise_generator.rb b/lib/generators/mongoid/devise_generator.rb index 0270a740..9b037a32 100644 --- a/lib/generators/mongoid/devise_generator.rb +++ b/lib/generators/mongoid/devise_generator.rb @@ -37,9 +37,6 @@ module Mongoid field :current_sign_in_ip, :type => String field :last_sign_in_ip, :type => String - ## Encryptable - # field :password_salt, :type => String - ## Confirmable # field :confirmation_token, :type => String # field :confirmed_at, :type => Time diff --git a/test/encryptors_test.rb b/test/encryptors_test.rb deleted file mode 100644 index 199d29c4..00000000 --- a/test/encryptors_test.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'test_helper' - -class Encryptors < ActiveSupport::TestCase - test 'should match a password created by authlogic' do - authlogic = "b623c3bc9c775b0eb8edb218a382453396fec4146422853e66ecc4b6bc32d7162ee42074dcb5f180a770dc38b5df15812f09bbf497a4a1b95fe5e7d2b8eb7eb4" - encryptor = Devise::Encryptors::AuthlogicSha512.digest('123mudar', 20, 'usZK_z_EAaF61Gwkw-ed', '') - assert_equal authlogic, encryptor - end - - test 'should match a password created by restful_authentication' do - restful_authentication = "93110f71309ce91366375ea44e2a6f5cc73fa8d4" - encryptor = Devise::Encryptors::RestfulAuthenticationSha1.digest('123mudar', 10, '48901d2b247a54088acb7f8ea3e695e50fe6791b', 'fee9a51ec0a28d11be380ca6dee6b4b760c1a3bf') - assert_equal restful_authentication, encryptor - end - - test 'should match a password created by clearance' do - clearance = "0f40bbae18ddefd7066276c3ef209d40729b0378" - encryptor = Devise::Encryptors::ClearanceSha1.digest('123mudar', nil, '65c58472c207c829f28c68619d3e3aefed18ab3f', nil) - assert_equal clearance, encryptor - end - - Devise::ENCRYPTORS_LENGTH.each do |key, value| - test "should have length #{value} for #{key.inspect}" do - swap Devise, :encryptor => key do - encryptor = Devise::Encryptors.const_get(key.to_s.classify) - assert_equal value, encryptor.digest('a', 4, encryptor.salt(4), nil).size - end - end - end -end diff --git a/test/models/encryptable_test.rb b/test/models/encryptable_test.rb deleted file mode 100644 index 3b30cc9e..00000000 --- a/test/models/encryptable_test.rb +++ /dev/null @@ -1,73 +0,0 @@ -require 'test_helper' - -class EncryptableTest < ActiveSupport::TestCase - def encrypt_password(admin, pepper=Admin.pepper, stretches=Admin.stretches, encryptor=Admin.encryptor_class) - encryptor.digest('123456', stretches, admin.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 generate salt while setting password' do - assert_present create_admin.password_salt - end - - test 'should not change password salt when updating' do - admin = create_admin - salt = admin.password_salt - admin.expects(:password_salt=).never - admin.save! - assert_equal salt, admin.password_salt - end - - test 'should generate a base64 hash using SecureRandom for password salt' do - swap_with_encryptor Admin, :sha1 do - SecureRandom.expects(:base64).with(15).returns('01lI').twice - salt = create_admin.password_salt - assert_not_equal '01lI', salt - assert_equal 4, salt.size - end - end - - test 'should not generate salt if password is blank' do - assert_blank create_admin(:password => nil).password_salt - assert_blank create_admin(:password => '').password_salt - end - - test 'should encrypt password again if password has changed' do - admin = create_admin - encrypted_password = admin.encrypted_password - admin.password = admin.password_confirmation = 'new_password' - admin.save! - assert_not_equal encrypted_password, admin.encrypted_password - end - - test 'should respect encryptor configuration' do - swap_with_encryptor Admin, :sha512 do - admin = create_admin - assert_equal admin.encrypted_password, encrypt_password(admin, Admin.pepper, Admin.stretches, Devise::Encryptors::Sha512) - end - end - - test 'should not validate password when salt is nil' do - admin = create_admin - admin.password_salt = nil - admin.save - assert_not admin.valid_password?('123456') - end - - test 'required_fields should contain the fields that Devise uses' do - assert_same_content Devise::Models::Encryptable.required_fields(User), [ - :password_salt - ] - end -end diff --git a/test/models_test.rb b/test/models_test.rb index ebbbbe4d..cea5ee75 100644 --- a/test/models_test.rb +++ b/test/models_test.rb @@ -1,7 +1,7 @@ require 'test_helper' class Configurable < User - devise :database_authenticatable, :encryptable, :confirmable, :rememberable, :timeoutable, :lockable, + devise :database_authenticatable, :confirmable, :rememberable, :timeoutable, :lockable, :stretches => 15, :pepper => 'abcdef', :allow_unconfirmed_access_for => 5.days, :remember_for => 7.days, :timeout_in => 15.minutes, :unlock_in => 10.days end @@ -39,7 +39,7 @@ class ActiveRecordTest < ActiveSupport::TestCase end test 'can cherry pick modules' do - assert_include_modules Admin, :database_authenticatable, :registerable, :timeoutable, :recoverable, :lockable, :encryptable, :confirmable + assert_include_modules Admin, :database_authenticatable, :registerable, :timeoutable, :recoverable, :lockable, :confirmable end test 'validations options are not applied too late' do @@ -55,12 +55,12 @@ class ActiveRecordTest < ActiveSupport::TestCase end test 'chosen modules are inheritable' do - assert_include_modules Inheritable, :database_authenticatable, :registerable, :timeoutable, :recoverable, :lockable, :encryptable, :confirmable + assert_include_modules Inheritable, :database_authenticatable, :registerable, :timeoutable, :recoverable, :lockable, :confirmable end test 'order of module inclusion' do - correct_module_order = [:database_authenticatable, :encryptable, :recoverable, :registerable, :confirmable, :lockable, :timeoutable] - incorrect_module_order = [:database_authenticatable, :timeoutable, :registerable, :recoverable, :lockable, :encryptable, :confirmable] + correct_module_order = [:database_authenticatable, :recoverable, :registerable, :confirmable, :lockable, :timeoutable] + incorrect_module_order = [:database_authenticatable, :timeoutable, :registerable, :recoverable, :lockable, :confirmable] assert_include_modules Admin, *incorrect_module_order diff --git a/test/rails_app/app/mongoid/admin.rb b/test/rails_app/app/mongoid/admin.rb index beb60ff4..4ab74083 100644 --- a/test/rails_app/app/mongoid/admin.rb +++ b/test/rails_app/app/mongoid/admin.rb @@ -22,9 +22,6 @@ class Admin field :confirmation_sent_at, :type => Time field :unconfirmed_email, :type => String # Only if using reconfirmable - ## Encryptable - field :password_salt, :type => String - ## Lockable field :locked_at, :type => Time end diff --git a/test/rails_app/app/mongoid/user.rb b/test/rails_app/app/mongoid/user.rb index 0d85190b..5c5a3817 100644 --- a/test/rails_app/app/mongoid/user.rb +++ b/test/rails_app/app/mongoid/user.rb @@ -26,9 +26,6 @@ class User field :current_sign_in_ip, :type => String field :last_sign_in_ip, :type => String - ## Encryptable - # field :password_salt, :type => String - ## Confirmable field :confirmation_token, :type => String field :confirmed_at, :type => Time diff --git a/test/rails_app/config/initializers/devise.rb b/test/rails_app/config/initializers/devise.rb index 96987a8f..557dea36 100644 --- a/test/rails_app/config/initializers/devise.rb +++ b/test/rails_app/config/initializers/devise.rb @@ -129,14 +129,6 @@ Devise.setup do |config| # change their passwords. config.reset_password_within = 2.hours - # ==> Configuration for :encryptable - # Allow you to use another encryption algorithm besides bcrypt (default). You can use - # :sha1, :sha512 or 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 = :sha512 - # Setup a pepper to generate the encrypted password. config.pepper = "d142367154e5beacca404b1a6a4f8bc52c6fdcfa3ccc3cf8eb49f3458a688ee6ac3b9fae488432a3bfca863b8a90008368a9f3a3dfbe5a962e64b6ab8f3a3a1a" diff --git a/test/rails_app/db/migrate/20100401102949_create_tables.rb b/test/rails_app/db/migrate/20100401102949_create_tables.rb index c435b15f..c7d7210b 100644 --- a/test/rails_app/db/migrate/20100401102949_create_tables.rb +++ b/test/rails_app/db/migrate/20100401102949_create_tables.rb @@ -22,9 +22,6 @@ class CreateTables < ActiveRecord::Migration t.string :current_sign_in_ip t.string :last_sign_in_ip - ## Encryptable - # t.string :password_salt - ## Confirmable t.string :confirmation_token t.datetime :confirmed_at @@ -60,9 +57,6 @@ class CreateTables < ActiveRecord::Migration t.datetime :confirmation_sent_at t.string :unconfirmed_email # Only if using reconfirmable - ## Encryptable - t.string :password_salt - ## Lockable t.datetime :locked_at diff --git a/test/rails_app/lib/shared_admin.rb b/test/rails_app/lib/shared_admin.rb index 561562d7..5b38341e 100644 --- a/test/rails_app/lib/shared_admin.rb +++ b/test/rails_app/lib/shared_admin.rb @@ -2,7 +2,7 @@ module SharedAdmin extend ActiveSupport::Concern included do - devise :database_authenticatable, :encryptable, :registerable, + devise :database_authenticatable, :registerable, :timeoutable, :recoverable, :lockable, :confirmable, :unlock_strategy => :time, :lock_strategy => :none, :allow_unconfirmed_access_for => 2.weeks, :reconfirmable => true