devise-encryptable 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/.travis.yml +9 -0
- data/Changelog.md +4 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +134 -0
- data/LICENSE +201 -0
- data/README.md +43 -0
- data/Rakefile +11 -0
- data/devise-encryptable.gemspec +19 -0
- data/gemfiles/Gemfile.rails-3.1.x +15 -0
- data/gemfiles/Gemfile.rails-3.1.x.lock +136 -0
- data/lib/devise-encryptable.rb +1 -0
- data/lib/devise/encryptable/encryptable.rb +28 -0
- data/lib/devise/encryptable/encryptors/authlogic_sha512.rb +21 -0
- data/lib/devise/encryptable/encryptors/base.rb +26 -0
- data/lib/devise/encryptable/encryptors/clearance_sha1.rb +19 -0
- data/lib/devise/encryptable/encryptors/restful_authentication_sha1.rb +24 -0
- data/lib/devise/encryptable/encryptors/sha1.rb +27 -0
- data/lib/devise/encryptable/encryptors/sha512.rb +27 -0
- data/lib/devise/encryptable/model.rb +86 -0
- data/lib/devise/encryptable/version.rb +5 -0
- data/test/devise/encryptable/encryptable_test.rb +65 -0
- data/test/devise/encryptable/encryptors_test.rb +32 -0
- data/test/rails_app/.gitignore +15 -0
- data/test/rails_app/Rakefile +7 -0
- data/test/rails_app/app/models/.gitkeep +0 -0
- data/test/rails_app/app/models/admin.rb +5 -0
- data/test/rails_app/app/models/user.rb +5 -0
- data/test/rails_app/config.ru +4 -0
- data/test/rails_app/config/application.rb +59 -0
- data/test/rails_app/config/boot.rb +6 -0
- data/test/rails_app/config/database.yml +3 -0
- data/test/rails_app/config/environment.rb +5 -0
- data/test/rails_app/config/environments/development.rb +37 -0
- data/test/rails_app/config/environments/production.rb +67 -0
- data/test/rails_app/config/environments/test.rb +37 -0
- data/test/rails_app/config/initializers/devise.rb +14 -0
- data/test/rails_app/db/migrate/20120508165529_create_tables.rb +77 -0
- data/test/support/assertions.rb +14 -0
- data/test/support/factories.rb +22 -0
- data/test/support/swappers.rb +28 -0
- data/test/test_helper.rb +21 -0
- metadata +126 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem "devise-encryptable", :path => ".."
|
4
|
+
|
5
|
+
gem 'devise', :git => "git://github.com/plataformatec/devise.git", :branch => "removing_encryptable"
|
6
|
+
gem 'minitest'
|
7
|
+
gem 'rails', "3.1.4"
|
8
|
+
gem 'sqlite3'
|
9
|
+
|
10
|
+
gem 'mocha', :require => false
|
11
|
+
|
12
|
+
gem 'pry'
|
13
|
+
gem 'pry-doc'
|
14
|
+
gem 'pry-nav'
|
15
|
+
gem 'awesome_print'
|
@@ -0,0 +1,136 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/plataformatec/devise.git
|
3
|
+
revision: 0d868b9ec1e0c7d3a041bb65c2889b5bd23d65dc
|
4
|
+
branch: removing_encryptable
|
5
|
+
specs:
|
6
|
+
devise (2.1.0.rc)
|
7
|
+
bcrypt-ruby (~> 3.0)
|
8
|
+
orm_adapter (~> 0.0.7)
|
9
|
+
railties (~> 3.1)
|
10
|
+
warden (~> 1.1.1)
|
11
|
+
|
12
|
+
PATH
|
13
|
+
remote: ..
|
14
|
+
specs:
|
15
|
+
devise-encryptable (0.0.1)
|
16
|
+
devise (~> 2.1.0.rc)
|
17
|
+
|
18
|
+
GEM
|
19
|
+
remote: https://rubygems.org/
|
20
|
+
specs:
|
21
|
+
actionmailer (3.1.4)
|
22
|
+
actionpack (= 3.1.4)
|
23
|
+
mail (~> 2.3.0)
|
24
|
+
actionpack (3.1.4)
|
25
|
+
activemodel (= 3.1.4)
|
26
|
+
activesupport (= 3.1.4)
|
27
|
+
builder (~> 3.0.0)
|
28
|
+
erubis (~> 2.7.0)
|
29
|
+
i18n (~> 0.6)
|
30
|
+
rack (~> 1.3.6)
|
31
|
+
rack-cache (~> 1.1)
|
32
|
+
rack-mount (~> 0.8.2)
|
33
|
+
rack-test (~> 0.6.1)
|
34
|
+
sprockets (~> 2.0.3)
|
35
|
+
activemodel (3.1.4)
|
36
|
+
activesupport (= 3.1.4)
|
37
|
+
builder (~> 3.0.0)
|
38
|
+
i18n (~> 0.6)
|
39
|
+
activerecord (3.1.4)
|
40
|
+
activemodel (= 3.1.4)
|
41
|
+
activesupport (= 3.1.4)
|
42
|
+
arel (~> 2.2.3)
|
43
|
+
tzinfo (~> 0.3.29)
|
44
|
+
activeresource (3.1.4)
|
45
|
+
activemodel (= 3.1.4)
|
46
|
+
activesupport (= 3.1.4)
|
47
|
+
activesupport (3.1.4)
|
48
|
+
multi_json (~> 1.0)
|
49
|
+
arel (2.2.3)
|
50
|
+
awesome_print (1.0.2)
|
51
|
+
bcrypt-ruby (3.0.1)
|
52
|
+
builder (3.0.0)
|
53
|
+
coderay (1.0.6)
|
54
|
+
erubis (2.7.0)
|
55
|
+
hike (1.2.1)
|
56
|
+
i18n (0.6.0)
|
57
|
+
json (1.7.1)
|
58
|
+
mail (2.3.3)
|
59
|
+
i18n (>= 0.4.0)
|
60
|
+
mime-types (~> 1.16)
|
61
|
+
treetop (~> 1.4.8)
|
62
|
+
metaclass (0.0.1)
|
63
|
+
method_source (0.7.1)
|
64
|
+
mime-types (1.18)
|
65
|
+
minitest (3.0.0)
|
66
|
+
mocha (0.11.4)
|
67
|
+
metaclass (~> 0.0.1)
|
68
|
+
multi_json (1.3.4)
|
69
|
+
orm_adapter (0.0.7)
|
70
|
+
polyglot (0.3.3)
|
71
|
+
pry (0.9.9.4)
|
72
|
+
coderay (~> 1.0.5)
|
73
|
+
method_source (~> 0.7.1)
|
74
|
+
slop (>= 2.4.4, < 3)
|
75
|
+
pry-doc (0.4.1)
|
76
|
+
pry (>= 0.9.0)
|
77
|
+
yard (~> 0.7.4)
|
78
|
+
pry-nav (0.2.1)
|
79
|
+
pry (~> 0.9.9)
|
80
|
+
rack (1.3.6)
|
81
|
+
rack-cache (1.2)
|
82
|
+
rack (>= 0.4)
|
83
|
+
rack-mount (0.8.3)
|
84
|
+
rack (>= 1.0.0)
|
85
|
+
rack-ssl (1.3.2)
|
86
|
+
rack
|
87
|
+
rack-test (0.6.1)
|
88
|
+
rack (>= 1.0)
|
89
|
+
rails (3.1.4)
|
90
|
+
actionmailer (= 3.1.4)
|
91
|
+
actionpack (= 3.1.4)
|
92
|
+
activerecord (= 3.1.4)
|
93
|
+
activeresource (= 3.1.4)
|
94
|
+
activesupport (= 3.1.4)
|
95
|
+
bundler (~> 1.0)
|
96
|
+
railties (= 3.1.4)
|
97
|
+
railties (3.1.4)
|
98
|
+
actionpack (= 3.1.4)
|
99
|
+
activesupport (= 3.1.4)
|
100
|
+
rack-ssl (~> 1.3.2)
|
101
|
+
rake (>= 0.8.7)
|
102
|
+
rdoc (~> 3.4)
|
103
|
+
thor (~> 0.14.6)
|
104
|
+
rake (0.9.2.2)
|
105
|
+
rdoc (3.12)
|
106
|
+
json (~> 1.4)
|
107
|
+
slop (2.4.4)
|
108
|
+
sprockets (2.0.4)
|
109
|
+
hike (~> 1.2)
|
110
|
+
rack (~> 1.0)
|
111
|
+
tilt (~> 1.1, != 1.3.0)
|
112
|
+
sqlite3 (1.3.6)
|
113
|
+
thor (0.14.6)
|
114
|
+
tilt (1.3.3)
|
115
|
+
treetop (1.4.10)
|
116
|
+
polyglot
|
117
|
+
polyglot (>= 0.3.1)
|
118
|
+
tzinfo (0.3.33)
|
119
|
+
warden (1.1.1)
|
120
|
+
rack (>= 1.0)
|
121
|
+
yard (0.7.5)
|
122
|
+
|
123
|
+
PLATFORMS
|
124
|
+
ruby
|
125
|
+
|
126
|
+
DEPENDENCIES
|
127
|
+
awesome_print
|
128
|
+
devise!
|
129
|
+
devise-encryptable!
|
130
|
+
minitest
|
131
|
+
mocha
|
132
|
+
pry
|
133
|
+
pry-doc
|
134
|
+
pry-nav
|
135
|
+
rails (= 3.1.4)
|
136
|
+
sqlite3
|
@@ -0,0 +1 @@
|
|
1
|
+
require "devise/encryptable/encryptable"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Devise
|
2
|
+
|
3
|
+
# Declare encryptors length which are used in migrations.
|
4
|
+
ENCRYPTORS_LENGTH = {
|
5
|
+
:sha1 => 40,
|
6
|
+
:sha512 => 128,
|
7
|
+
:clearance_sha1 => 40,
|
8
|
+
:restful_authentication_sha1 => 40,
|
9
|
+
:authlogic_sha512 => 128
|
10
|
+
}
|
11
|
+
|
12
|
+
# Used to define the password encryption algorithm.
|
13
|
+
mattr_accessor :encryptor
|
14
|
+
@@encryptor = nil
|
15
|
+
|
16
|
+
module Encryptable
|
17
|
+
module Encryptors
|
18
|
+
autoload :AuthlogicSha512, 'devise/encryptable/encryptors/authlogic_sha512'
|
19
|
+
autoload :Base, 'devise/encryptable/encryptors/base'
|
20
|
+
autoload :ClearanceSha1, 'devise/encryptable/encryptors/clearance_sha1'
|
21
|
+
autoload :RestfulAuthenticationSha1, 'devise/encryptable/encryptors/restful_authentication_sha1'
|
22
|
+
autoload :Sha1, 'devise/encryptable/encryptors/sha1'
|
23
|
+
autoload :Sha512, 'devise/encryptable/encryptors/sha512'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Devise.add_module(:encryptable, :model => 'devise/encryptable/model')
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "digest/sha2"
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Encryptable
|
5
|
+
module Encryptors
|
6
|
+
# = AuthlogicSha512
|
7
|
+
# Simulates Authlogic's default encryption mechanism.
|
8
|
+
# Warning: it uses Devise's stretches configuration to port Authlogic's one. Should be set to 20 in the initializer to simulate
|
9
|
+
# the default behavior.
|
10
|
+
class AuthlogicSha512 < Base
|
11
|
+
# Generates a default password digest based on salt, pepper and the
|
12
|
+
# incoming password.
|
13
|
+
def self.digest(password, stretches, salt, pepper)
|
14
|
+
digest = [password, salt].flatten.join('')
|
15
|
+
stretches.times { digest = Digest::SHA512.hexdigest(digest) }
|
16
|
+
digest
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Devise
|
2
|
+
# Implements a way of adding different encryptions.
|
3
|
+
# The class should implement a self.digest method that taks the following params:
|
4
|
+
# - password
|
5
|
+
# - stretches: the number of times the encryption will be applied
|
6
|
+
# - salt: the password salt as defined by devise
|
7
|
+
# - pepper: Devise config option
|
8
|
+
#
|
9
|
+
module Encryptable
|
10
|
+
module Encryptors
|
11
|
+
class Base
|
12
|
+
def self.digest
|
13
|
+
raise NotImplemented
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.salt(stretches)
|
17
|
+
Devise.friendly_token[0,20]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.compare(encrypted_password, password, stretches, salt, pepper)
|
21
|
+
Devise.secure_compare(encrypted_password, digest(password, stretches, salt, pepper))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "digest/sha1"
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Encryptable
|
5
|
+
module Encryptors
|
6
|
+
# = ClearanceSha1
|
7
|
+
# Simulates Clearance's default encryption mechanism.
|
8
|
+
# Warning: it uses Devise's pepper to port the concept of REST_AUTH_SITE_KEY
|
9
|
+
# Warning: it uses Devise's stretches configuration to port the concept of REST_AUTH_DIGEST_STRETCHES
|
10
|
+
class ClearanceSha1 < Base
|
11
|
+
# Generates a default password digest based on salt, pepper and the
|
12
|
+
# incoming password.
|
13
|
+
def self.digest(password, stretches, salt, pepper)
|
14
|
+
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "digest/sha1"
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Encryptable
|
5
|
+
module Encryptors
|
6
|
+
# = RestfulAuthenticationSha1
|
7
|
+
# Simulates Restful Authentication's default encryption mechanism.
|
8
|
+
# Warning: it uses Devise's pepper to port the concept of REST_AUTH_SITE_KEY
|
9
|
+
# Warning: it uses Devise's stretches configuration to port the concept of REST_AUTH_DIGEST_STRETCHES. Should be set to 10 in
|
10
|
+
# the initializer to simulate the default behavior.
|
11
|
+
class RestfulAuthenticationSha1 < Base
|
12
|
+
|
13
|
+
# Generates a default password digest based on salt, pepper and the
|
14
|
+
# incoming password.
|
15
|
+
def self.digest(password, stretches, salt, pepper)
|
16
|
+
digest = pepper
|
17
|
+
stretches.times { digest = Digest::SHA1.hexdigest([digest, salt, password, pepper].flatten.join('--')) }
|
18
|
+
digest
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "digest/sha1"
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Encryptable
|
5
|
+
module Encryptors
|
6
|
+
# = Sha1
|
7
|
+
# Uses the Sha1 hash algorithm to encrypt passwords.
|
8
|
+
class Sha1 < Base
|
9
|
+
# Generates a default password digest based on stretches, salt, pepper and the
|
10
|
+
# incoming password.
|
11
|
+
def self.digest(password, stretches, salt, pepper)
|
12
|
+
digest = pepper
|
13
|
+
stretches.times { digest = self.secure_digest(salt, digest, password, pepper) }
|
14
|
+
digest
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Generate a SHA1 digest joining args. Generated token is something like
|
20
|
+
# --arg1--arg2--arg3--argN--
|
21
|
+
def self.secure_digest(*tokens)
|
22
|
+
::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "digest/sha2"
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Encryptable
|
5
|
+
module Encryptors
|
6
|
+
# = Sha512
|
7
|
+
# Uses the Sha512 hash algorithm to encrypt passwords.
|
8
|
+
class Sha512 < Base
|
9
|
+
# Generates a default password digest based on salt, pepper and the
|
10
|
+
# incoming password.
|
11
|
+
def self.digest(password, stretches, salt, pepper)
|
12
|
+
digest = pepper
|
13
|
+
stretches.times { digest = self.secure_digest(salt, digest, password, pepper) }
|
14
|
+
digest
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Generate a Sha512 digest joining args. Generated token is something like
|
20
|
+
# --arg1--arg2--arg3--argN--
|
21
|
+
def self.secure_digest(*tokens)
|
22
|
+
::Digest::SHA512.hexdigest('--' << tokens.flatten.join('--') << '--')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'devise/strategies/database_authenticatable'
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Models
|
5
|
+
# Encryptable module adds support to several encryptors wrapping
|
6
|
+
# them in a salt and pepper mechanism to increase security.
|
7
|
+
#
|
8
|
+
# == Options
|
9
|
+
#
|
10
|
+
# Encryptable adds the following options to devise_for:
|
11
|
+
#
|
12
|
+
# * +pepper+: a random string used to provide a more secure hash.
|
13
|
+
#
|
14
|
+
# * +encryptor+: the encryptor going to be used. By default is nil.
|
15
|
+
#
|
16
|
+
# == Examples
|
17
|
+
#
|
18
|
+
# User.find(1).valid_password?('password123') # returns true/false
|
19
|
+
#
|
20
|
+
module Encryptable
|
21
|
+
extend ActiveSupport::Concern
|
22
|
+
|
23
|
+
included do
|
24
|
+
attr_reader :password, :current_password
|
25
|
+
attr_accessor :password_confirmation
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.required_fields(klass)
|
29
|
+
[:password_salt]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Generates password salt when setting the password.
|
33
|
+
def password=(new_password)
|
34
|
+
self.password_salt = self.class.password_salt if new_password.present?
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
# Validates the password considering the salt.
|
39
|
+
def valid_password?(password)
|
40
|
+
return false if encrypted_password.blank?
|
41
|
+
encryptor_class.compare(encrypted_password, password, self.class.stretches, authenticatable_salt, self.class.pepper)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Overrides authenticatable salt to use the new password_salt
|
45
|
+
# column. authenticatable_salt is used by `valid_password?`
|
46
|
+
# and by other modules whenever there is a need for a random
|
47
|
+
# token based on the user password.
|
48
|
+
def authenticatable_salt
|
49
|
+
self.password_salt
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
# Digests the password using the configured encryptor.
|
55
|
+
def password_digest(password)
|
56
|
+
if password_salt.present?
|
57
|
+
encryptor_class.digest(password, self.class.stretches, authenticatable_salt, self.class.pepper)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def encryptor_class
|
62
|
+
self.class.encryptor_class
|
63
|
+
end
|
64
|
+
|
65
|
+
module ClassMethods
|
66
|
+
Devise::Models.config(self, :encryptor)
|
67
|
+
|
68
|
+
# Returns the class for the configured encryptor.
|
69
|
+
def encryptor_class
|
70
|
+
@encryptor_class ||= case encryptor
|
71
|
+
when :bcrypt
|
72
|
+
raise "In order to use bcrypt as encryptor, simply remove :encryptable from your devise model"
|
73
|
+
when nil
|
74
|
+
raise "You need to give an :encryptor as option in order to use :encryptable"
|
75
|
+
else
|
76
|
+
Devise::Encryptable::Encryptors.const_get(encryptor.to_s.classify)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def password_salt
|
81
|
+
self.encryptor_class.salt(self.stretches)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class EncryptableTest < ActiveSupport::TestCase
|
4
|
+
include Support::Assertions
|
5
|
+
include Support::Factories
|
6
|
+
include Support::Swappers
|
7
|
+
|
8
|
+
def encrypt_password(admin, pepper=Admin.pepper, stretches=Admin.stretches, encryptor=Admin.encryptor_class)
|
9
|
+
encryptor.digest('123456', stretches, admin.password_salt, pepper)
|
10
|
+
end
|
11
|
+
|
12
|
+
test 'should generate salt while setting password' do
|
13
|
+
assert_present create_admin.password_salt
|
14
|
+
end
|
15
|
+
|
16
|
+
test 'should not change password salt when updating' do
|
17
|
+
admin = create_admin
|
18
|
+
salt = admin.password_salt
|
19
|
+
admin.expects(:password_salt=).never
|
20
|
+
admin.save!
|
21
|
+
assert_equal salt, admin.password_salt
|
22
|
+
end
|
23
|
+
|
24
|
+
test 'should generate a base64 hash using SecureRandom for password salt' do
|
25
|
+
swap_with_encryptor Admin, :sha1 do
|
26
|
+
SecureRandom.expects(:base64).with(15).returns('01lI').once
|
27
|
+
salt = create_admin.password_salt
|
28
|
+
assert_not_equal '01lI', salt
|
29
|
+
assert_equal 4, salt.size
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
test 'should not generate salt if password is blank' do
|
34
|
+
assert_blank create_admin(:password => nil).password_salt
|
35
|
+
assert_blank create_admin(:password => '').password_salt
|
36
|
+
end
|
37
|
+
|
38
|
+
test 'should encrypt password again if password has changed' do
|
39
|
+
admin = create_admin
|
40
|
+
encrypted_password = admin.encrypted_password
|
41
|
+
admin.password = admin.password_confirmation = 'new_password'
|
42
|
+
admin.save!
|
43
|
+
assert_not_equal encrypted_password, admin.encrypted_password
|
44
|
+
end
|
45
|
+
|
46
|
+
test 'should respect encryptor configuration' do
|
47
|
+
swap_with_encryptor Admin, :sha512 do
|
48
|
+
admin = create_admin
|
49
|
+
assert_equal admin.encrypted_password, encrypt_password(admin, Admin.pepper, Admin.stretches, Devise::Encryptable::Encryptors::Sha512)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
test 'should not validate password when salt is nil' do
|
54
|
+
admin = create_admin
|
55
|
+
admin.password_salt = nil
|
56
|
+
admin.save
|
57
|
+
assert_not admin.valid_password?('123456')
|
58
|
+
end
|
59
|
+
|
60
|
+
test 'required_fields should contain the fields that Devise uses' do
|
61
|
+
assert_same_content Devise::Models::Encryptable.required_fields(Admin), [
|
62
|
+
:password_salt
|
63
|
+
]
|
64
|
+
end
|
65
|
+
end
|