Skip to content

Commit

Permalink
Add rolling option
Browse files Browse the repository at this point in the history
  • Loading branch information
sobrinho committed Apr 30, 2020
1 parent 2517353 commit ceb9c5a
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 10 deletions.
4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### master

* Add support to roll legacy encryptors

### 0.2.0

* Updated to Rails 4+.
Expand Down
4 changes: 4 additions & 0 deletions lib/devise/encryptable/encryptable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ module Devise
mattr_accessor :encryptor
@@encryptor = nil

# Used to define a legacy algorithm that needs to be rolled to a new one.
mattr_accessor :transition_from_encryptor
@@transition_from_encryptor = nil

module Encryptable
module Encryptors
autoload :AuthlogicSha512, 'devise/encryptable/encryptors/authlogic_sha512'
Expand Down
54 changes: 44 additions & 10 deletions lib/devise/encryptable/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ module Models
#
# * +encryptor+: the encryptor going to be used. By default is nil.
#
# * +transition_from_encryptor+: the legacy encryptor that needs to rolled to the +encryptor+.
#
# == Examples
#
# User.find(1).valid_password?('password123') # returns true/false
# User.find(1).valid_password?('password123')
# #=> returns true/false
# #=> if true and using a legacy encryptor, it will update the user to the new encryptor.
#
module Encryptable
extend ActiveSupport::Concern
Expand All @@ -38,7 +42,21 @@ def password=(new_password)
# 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)

encryptor_arguments = [
encrypted_password,
password,
self.class.stretches,
authenticatable_salt,
self.class.pepper
]

if transition_from_encryptor_class.try(:compare, *encryptor_arguments)
update_attribute(:password, password)
return true
end

encryptor_class.compare(*encryptor_arguments)
end

# Overrides authenticatable salt to use the new password_salt
Expand All @@ -62,24 +80,40 @@ def encryptor_class
self.class.encryptor_class
end

def transition_from_encryptor_class
self.class.transition_from_encryptor_class
end

module ClassMethods
Devise::Models.config(self, :encryptor)
Devise::Models.config(self, :transition_from_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::Encryptable::Encryptors.const_get(encryptor.to_s.classify)
end
@encryptor_class ||= compute_encryptor_class(encryptor)
end

# Returns the class for the configured transition from encryptor.
def transition_from_encryptor_class
@transition_from_encryptor_class ||= compute_encryptor_class(transition_from_encryptor) if transition_from_encryptor
end

def password_salt
self.encryptor_class.salt(self.stretches)
end

private

def compute_encryptor_class(encryptor)
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::Encryptable::Encryptors.const_get(encryptor.to_s.classify)
end
end
end
end
end
Expand Down
15 changes: 15 additions & 0 deletions test/devise/encryptable/encryptable_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ def encrypt_password(admin, pepper=Admin.pepper, stretches=Admin.stretches, encr
assert_not_equal encrypted_password, admin.encrypted_password
end

test 'should roll from legacy encryptor to the new one' do
admin = nil

swap_with_encryptor Admin, :sha1 do
admin = create_admin
assert admin.valid_password?('123456')
assert_equal admin.encrypted_password, encrypt_password(admin, Admin.pepper, Admin.stretches, Devise::Encryptable::Encryptors::Sha1)
end

swap_with_encryptor Admin, :sha512, transition_from_encryptor: :sha1 do
assert admin.valid_password?('123456')
assert_equal admin.encrypted_password, encrypt_password(admin, Admin.pepper, Admin.stretches, Devise::Encryptable::Encryptors::Sha512)
end
end

test 'should respect encryptor configuration' do
swap_with_encryptor Admin, :sha512 do
admin = create_admin
Expand Down
1 change: 1 addition & 0 deletions test/support/swappers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module Support
module Swappers
def swap_with_encryptor(klass, encryptor, options={})
klass.instance_variable_set(:@encryptor_class, nil)
klass.instance_variable_set(:@transition_from_encryptor, nil)

swap klass, options.merge(:encryptor => encryptor) do
begin
Expand Down

0 comments on commit ceb9c5a

Please sign in to comment.