Merge branch 'removing_encryptable'

This commit is contained in:
Rodrigo Flores 2012-05-09 18:05:14 -03:00
commit 1698f0f57c
22 changed files with 15 additions and 378 deletions

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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