devise 2.1.0.rc → 2.1.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of devise might be problematic. Click here for more details.
- data/CHANGELOG.rdoc +23 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +4 -6
- data/MIT-LICENSE +1 -1
- data/README.md +7 -3
- data/Rakefile +1 -1
- data/app/controllers/devise/sessions_controller.rb +2 -4
- data/app/controllers/devise/unlocks_controller.rb +15 -2
- data/app/controllers/devise_controller.rb +24 -11
- data/devise.gemspec +1 -1
- data/gemfiles/Gemfile.rails-3.1.x +1 -1
- data/gemfiles/Gemfile.rails-3.1.x.lock +38 -40
- data/lib/devise.rb +13 -55
- data/lib/devise/controllers/helpers.rb +0 -5
- data/lib/devise/failure_app.rb +3 -1
- data/lib/devise/hooks/lockable.rb +7 -0
- data/lib/devise/hooks/timeoutable.rb +1 -0
- data/lib/devise/models.rb +9 -3
- data/lib/devise/models/authenticatable.rb +13 -3
- data/lib/devise/models/confirmable.rb +2 -5
- data/lib/devise/models/database_authenticatable.rb +4 -6
- data/lib/devise/models/lockable.rb +6 -6
- data/lib/devise/models/rememberable.rb +3 -3
- data/lib/devise/models/token_authenticatable.rb +4 -1
- data/lib/devise/modules.rb +0 -1
- data/lib/devise/orm/active_record.rb +1 -42
- data/lib/devise/orm/mongoid.rb +1 -29
- data/lib/devise/rails.rb +1 -58
- data/lib/devise/rails/routes.rb +1 -1
- data/lib/devise/rails/warden_compat.rb +10 -4
- data/lib/devise/strategies/rememberable.rb +1 -1
- data/lib/devise/test_helpers.rb +48 -9
- data/lib/devise/version.rb +1 -1
- data/lib/generators/active_record/devise_generator.rb +8 -4
- data/lib/generators/devise/orm_helpers.rb +2 -1
- data/lib/generators/mongoid/devise_generator.rb +0 -3
- data/lib/generators/templates/devise.rb +1 -8
- data/test/controllers/custom_strategy_test.rb +62 -0
- data/test/controllers/sessions_controller_test.rb +21 -1
- data/test/failure_app_test.rb +13 -3
- data/test/generators/active_record_generator_test.rb +32 -0
- data/test/integration/authenticatable_test.rb +2 -2
- data/test/integration/recoverable_test.rb +13 -0
- data/test/integration/token_authenticatable_test.rb +13 -0
- data/test/models/lockable_test.rb +0 -9
- data/test/models/rememberable_test.rb +1 -2
- data/test/models_test.rb +5 -5
- data/test/rails_app/app/mongoid/admin.rb +0 -3
- data/test/rails_app/app/mongoid/user.rb +0 -3
- data/test/rails_app/config/initializers/devise.rb +0 -15
- data/test/rails_app/config/routes.rb +1 -0
- data/test/rails_app/db/migrate/20100401102949_create_tables.rb +0 -6
- data/test/rails_app/lib/shared_admin.rb +1 -1
- metadata +17 -24
- data/lib/devise/encryptors/authlogic_sha512.rb +0 -19
- data/lib/devise/encryptors/base.rb +0 -24
- data/lib/devise/encryptors/bcrypt.rb +0 -14
- data/lib/devise/encryptors/clearance_sha1.rb +0 -17
- data/lib/devise/encryptors/restful_authentication_sha1.rb +0 -22
- data/lib/devise/encryptors/sha1.rb +0 -25
- data/lib/devise/encryptors/sha512.rb +0 -25
- data/lib/devise/models/encryptable.rb +0 -80
- data/lib/devise/schema.rb +0 -109
- data/test/encryptors_test.rb +0 -30
- data/test/models/encryptable_test.rb +0 -73
data/lib/devise/test_helpers.rb
CHANGED
@@ -70,22 +70,61 @@ module Devise
|
|
70
70
|
def _catch_warden(&block)
|
71
71
|
result = catch(:warden, &block)
|
72
72
|
|
73
|
-
|
74
|
-
result[:action] ||= :unauthenticated
|
73
|
+
env = @controller.request.env
|
75
74
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
75
|
+
result ||= {}
|
76
|
+
|
77
|
+
# Set the response. In production, the rack result is returned
|
78
|
+
# from Warden::Manager#call, which the following is modelled on.
|
79
|
+
case result
|
80
|
+
when Array
|
81
|
+
if result.first == 401 && intercept_401?(env) # does this happen during testing?
|
82
|
+
_process_unauthenticated(env)
|
83
|
+
else
|
84
|
+
result
|
85
|
+
end
|
86
|
+
when Hash
|
87
|
+
_process_unauthenticated(env, result)
|
88
|
+
else
|
89
|
+
result
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def _process_unauthenticated(env, options = {})
|
94
|
+
options[:action] ||= :unauthenticated
|
95
|
+
proxy = env['warden']
|
96
|
+
result = options[:result] || proxy.result
|
97
|
+
|
98
|
+
ret = case result
|
99
|
+
when :redirect
|
100
|
+
body = proxy.message || "You are being redirected to #{proxy.headers['Location']}"
|
101
|
+
[proxy.status, proxy.headers, [body]]
|
102
|
+
when :custom
|
103
|
+
proxy.custom_response
|
104
|
+
else
|
105
|
+
env["PATH_INFO"] = "/#{options[:action]}"
|
106
|
+
env["warden.options"] = options
|
107
|
+
Warden::Manager._run_callbacks(:before_failure, env, options)
|
80
108
|
|
81
109
|
status, headers, body = Devise.warden_config[:failure_app].call(env).to_a
|
82
110
|
@controller.send :render, :status => status, :text => body,
|
83
111
|
:content_type => headers["Content-Type"], :location => headers["Location"]
|
112
|
+
nil # causes process return @response
|
113
|
+
end
|
84
114
|
|
85
|
-
|
86
|
-
|
87
|
-
|
115
|
+
# ensure that the controller response is set up. In production, this is
|
116
|
+
# not necessary since warden returns the results to rack. However, at
|
117
|
+
# testing time, we want the response to be available to the testing
|
118
|
+
# framework to verify what would be returned to rack.
|
119
|
+
if ret.is_a?(Array)
|
120
|
+
# ensure the controller response is set to our response.
|
121
|
+
@controller.response ||= @response
|
122
|
+
@response.status = ret.first
|
123
|
+
@response.headers = ret.second
|
124
|
+
@response.body = ret.third
|
88
125
|
end
|
126
|
+
|
127
|
+
ret
|
89
128
|
end
|
90
129
|
end
|
91
130
|
end
|
data/lib/devise/version.rb
CHANGED
@@ -22,10 +22,17 @@ module ActiveRecord
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def inject_devise_content
|
25
|
-
|
25
|
+
content = model_contents + <<CONTENT
|
26
26
|
# Setup accessible (or protected) attributes for your model
|
27
27
|
attr_accessible :email, :password, :password_confirmation, :remember_me
|
28
28
|
CONTENT
|
29
|
+
|
30
|
+
class_path = class_name.to_s.split("::")
|
31
|
+
|
32
|
+
indent_depth = class_path.size - 1
|
33
|
+
content = content.split("\n").map { |line| " " * indent_depth + line } .join("\n")
|
34
|
+
|
35
|
+
inject_into_class(model_path, class_path.last, content) if model_exists?
|
29
36
|
end
|
30
37
|
|
31
38
|
def migration_data
|
@@ -48,9 +55,6 @@ CONTENT
|
|
48
55
|
t.string :current_sign_in_ip
|
49
56
|
t.string :last_sign_in_ip
|
50
57
|
|
51
|
-
## Encryptable
|
52
|
-
# t.string :password_salt
|
53
|
-
|
54
58
|
## Confirmable
|
55
59
|
# t.string :confirmation_token
|
56
60
|
# t.datetime :confirmed_at
|
@@ -4,7 +4,8 @@ module Devise
|
|
4
4
|
def model_contents
|
5
5
|
<<-CONTENT
|
6
6
|
# Include default devise modules. Others available are:
|
7
|
-
# :token_authenticatable, :
|
7
|
+
# :token_authenticatable, :confirmable,
|
8
|
+
# :lockable, :timeoutable and :omniauthable
|
8
9
|
devise :database_authenticatable, :registerable,
|
9
10
|
:recoverable, :rememberable, :trackable, :validatable
|
10
11
|
|
@@ -37,9 +37,6 @@ module Mongoid
|
|
37
37
|
field :current_sign_in_ip, :type => String
|
38
38
|
field :last_sign_in_ip, :type => String
|
39
39
|
|
40
|
-
## Encryptable
|
41
|
-
# field :password_salt, :type => String
|
42
|
-
|
43
40
|
## Confirmable
|
44
41
|
# field :confirmation_token, :type => String
|
45
42
|
# field :confirmed_at, :type => Time
|
@@ -9,9 +9,6 @@ Devise.setup do |config|
|
|
9
9
|
# Configure the class responsible to send e-mails.
|
10
10
|
# config.mailer = "Devise::Mailer"
|
11
11
|
|
12
|
-
# Automatically apply schema changes in tableless databases
|
13
|
-
config.apply_schema = false
|
14
|
-
|
15
12
|
# ==> ORM configuration
|
16
13
|
# Load and configure the ORM. Supports :active_record (default) and
|
17
14
|
# :mongoid (bson_ext recommended) by default. Other ORMs may be
|
@@ -95,7 +92,7 @@ Devise.setup do |config|
|
|
95
92
|
# the user cannot access the website without confirming his account.
|
96
93
|
# config.allow_unconfirmed_access_for = 2.days
|
97
94
|
|
98
|
-
# If true, requires any email changes to be confirmed (
|
95
|
+
# If true, requires any email changes to be confirmed (exactly the same way as
|
99
96
|
# initial account confirmation) to be applied. Requires additional unconfirmed_email
|
100
97
|
# db field (see migrations). Until confirmed new email is stored in
|
101
98
|
# unconfirmed email column, and copied to email column on successful confirmation.
|
@@ -111,10 +108,6 @@ Devise.setup do |config|
|
|
111
108
|
# If true, extends the user's remember period when remembered via cookie.
|
112
109
|
# config.extend_remember_period = false
|
113
110
|
|
114
|
-
# If true, uses the password salt as remember token. This should be turned
|
115
|
-
# to false if you are not using database authenticatable.
|
116
|
-
config.use_salt_as_remember_token = true
|
117
|
-
|
118
111
|
# Options to be passed to the created cookie. For instance, you can set
|
119
112
|
# :secure => true in order to force SSL only cookies.
|
120
113
|
# config.rememberable_options = {}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'warden/strategies/base'
|
4
|
+
require 'devise/test_helpers'
|
5
|
+
|
6
|
+
class CustomStrategyController < ActionController::Base
|
7
|
+
def new
|
8
|
+
warden.authenticate!(:custom_strategy)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# These tests are to prove that a warden strategy can successfully
|
13
|
+
# return a custom response, including a specific status code and
|
14
|
+
# custom http response headers. This does work in production,
|
15
|
+
# however, at the time of writing this, the Devise test helpers do
|
16
|
+
# not recognise the custom response and proceed to calling the
|
17
|
+
# Failure App. This makes it impossible to write tests for a
|
18
|
+
# strategy that return a custom response with Devise.
|
19
|
+
class CustomStrategy < Warden::Strategies::Base
|
20
|
+
def authenticate!
|
21
|
+
custom_headers = { "X-FOO" => "BAR" }
|
22
|
+
response = Rack::Response.new("BAD REQUEST", 400, custom_headers)
|
23
|
+
custom! response.finish
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class CustomStrategyTest < ActionController::TestCase
|
28
|
+
tests CustomStrategyController
|
29
|
+
|
30
|
+
include Devise::TestHelpers
|
31
|
+
|
32
|
+
setup do
|
33
|
+
Warden::Strategies.add(:custom_strategy, CustomStrategy)
|
34
|
+
end
|
35
|
+
|
36
|
+
teardown do
|
37
|
+
Warden::Strategies._strategies.delete(:custom_strategy)
|
38
|
+
end
|
39
|
+
|
40
|
+
test "custom strategy can return its own status code" do
|
41
|
+
ret = get :new
|
42
|
+
|
43
|
+
# check the returned rack array
|
44
|
+
assert ret.is_a?(Array)
|
45
|
+
assert_equal 400, ret.first
|
46
|
+
|
47
|
+
# check the saved response as well. This is purely so that the response is available to the testing framework
|
48
|
+
# for verification. In production, the above array would be delivered directly to Rack.
|
49
|
+
assert_response 400
|
50
|
+
end
|
51
|
+
|
52
|
+
test "custom strategy can return custom headers" do
|
53
|
+
ret = get :new
|
54
|
+
|
55
|
+
# check the returned rack array
|
56
|
+
assert ret.is_a?(Array)
|
57
|
+
assert_equal ret.third['X-FOO'], 'BAR'
|
58
|
+
|
59
|
+
# check the saved response headers as well.
|
60
|
+
assert_equal response.headers['X-FOO'], 'BAR'
|
61
|
+
end
|
62
|
+
end
|
@@ -13,4 +13,24 @@ class SessionsControllerTest < ActionController::TestCase
|
|
13
13
|
assert_equal 200, @response.status
|
14
14
|
assert_template "devise/sessions/new"
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
|
+
if defined?(ActiveRecord)
|
18
|
+
if ActiveRecord::Base.respond_to?(:mass_assignment_sanitizer)
|
19
|
+
test "#new doesn't raise mass-assignment exception even if sign-in key is attr_protected" do
|
20
|
+
request.env["devise.mapping"] = Devise.mappings[:user]
|
21
|
+
|
22
|
+
ActiveRecord::Base.mass_assignment_sanitizer = :strict
|
23
|
+
User.class_eval { attr_protected :email }
|
24
|
+
|
25
|
+
begin
|
26
|
+
assert_nothing_raised ActiveModel::MassAssignmentSecurity::Error do
|
27
|
+
get :new, :user => { :email => "allez viens!" }
|
28
|
+
end
|
29
|
+
ensure
|
30
|
+
ActiveRecord::Base.mass_assignment_sanitizer = :logger
|
31
|
+
User.class_eval { attr_accessible :email }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/test/failure_app_test.rb
CHANGED
@@ -29,20 +29,20 @@ class FailureTest < ActiveSupport::TestCase
|
|
29
29
|
end
|
30
30
|
|
31
31
|
context 'When redirecting' do
|
32
|
-
test '
|
32
|
+
test 'returns to the default redirect location' do
|
33
33
|
call_failure
|
34
34
|
assert_equal 302, @response.first
|
35
35
|
assert_equal 'You need to sign in or sign up before continuing.', @request.flash[:alert]
|
36
36
|
assert_equal 'http://test.host/users/sign_in', @response.second['Location']
|
37
37
|
end
|
38
38
|
|
39
|
-
test '
|
39
|
+
test 'returns to the default redirect location for wildcard requests' do
|
40
40
|
call_failure 'action_dispatch.request.formats' => nil, 'HTTP_ACCEPT' => '*/*'
|
41
41
|
assert_equal 302, @response.first
|
42
42
|
assert_equal 'http://test.host/users/sign_in', @response.second['Location']
|
43
43
|
end
|
44
44
|
|
45
|
-
test '
|
45
|
+
test 'returns to the root path if no session path is available' do
|
46
46
|
swap Devise, :router_name => :fake_app do
|
47
47
|
call_failure :app => RootFailureApp
|
48
48
|
assert_equal 302, @response.first
|
@@ -51,6 +51,16 @@ class FailureTest < ActiveSupport::TestCase
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
if Rails.application.config.respond_to?(:relative_url_root)
|
55
|
+
test 'returns to the default redirect location considering the relative url root' do
|
56
|
+
swap Rails.application.config, :relative_url_root => "/sample" do
|
57
|
+
call_failure
|
58
|
+
assert_equal 302, @response.first
|
59
|
+
assert_equal 'http://test.host/sample/users/sign_in', @response.second['Location']
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
54
64
|
test 'uses the proxy failure message as symbol' do
|
55
65
|
call_failure('warden' => OpenStruct.new(:message => :invalid))
|
56
66
|
assert_equal 'Invalid email or password.', @request.flash[:alert]
|
@@ -34,4 +34,36 @@ if DEVISE_ORM == :active_record
|
|
34
34
|
assert_no_migration "db/migrate/devise_create_monsters.rb"
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
module RailsEngine
|
39
|
+
class Engine < Rails::Engine
|
40
|
+
isolate_namespace RailsEngine
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def simulate_inside_engine(engine, namespace)
|
45
|
+
if Rails::Generators.respond_to?(:namespace=)
|
46
|
+
swap Rails::Generators, :namespace => namespace do
|
47
|
+
yield
|
48
|
+
end
|
49
|
+
else
|
50
|
+
swap Rails, :application => engine.instance do
|
51
|
+
yield
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class ActiveRecordEngineGeneratorTest < Rails::Generators::TestCase
|
57
|
+
tests ActiveRecord::Generators::DeviseGenerator
|
58
|
+
destination File.expand_path("../../tmp", __FILE__)
|
59
|
+
setup :prepare_destination
|
60
|
+
|
61
|
+
test "all files are properly created" do
|
62
|
+
simulate_inside_engine(RailsEngine::Engine, RailsEngine) do
|
63
|
+
run_generator ["monster"]
|
64
|
+
|
65
|
+
assert_file "app/models/rails_engine/monster.rb", /devise/,/attr_accessible (:[a-z_]+(, )?)+/
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
37
69
|
end
|
@@ -461,14 +461,14 @@ class AuthenticationOthersTest < ActionController::IntegrationTest
|
|
461
461
|
test 'sign out with xml format returns ok response' do
|
462
462
|
sign_in_as_user
|
463
463
|
get destroy_user_session_path(:format => 'xml')
|
464
|
-
assert_response :
|
464
|
+
assert_response :no_content
|
465
465
|
assert_not warden.authenticated?(:user)
|
466
466
|
end
|
467
467
|
|
468
468
|
test 'sign out with json format returns empty json response' do
|
469
469
|
sign_in_as_user
|
470
470
|
get destroy_user_session_path(:format => 'json')
|
471
|
-
assert_response :
|
471
|
+
assert_response :no_content
|
472
472
|
assert_not warden.authenticated?(:user)
|
473
473
|
end
|
474
474
|
end
|
@@ -284,4 +284,17 @@ class PasswordTest < ActionController::IntegrationTest
|
|
284
284
|
assert_current_url "/users/sign_in"
|
285
285
|
end
|
286
286
|
end
|
287
|
+
|
288
|
+
test "after recovering a password, should set failed attempts to 0" do
|
289
|
+
user = create_user
|
290
|
+
user.update_attribute(:failed_attempts, 10)
|
291
|
+
|
292
|
+
assert_equal 10, user.failed_attempts
|
293
|
+
request_forgot_password
|
294
|
+
reset_password :reset_password_token => user.reload.reset_password_token
|
295
|
+
|
296
|
+
assert warden.authenticated?(:user)
|
297
|
+
user.reload
|
298
|
+
assert_equal 0, user.failed_attempts
|
299
|
+
end
|
287
300
|
end
|
@@ -100,6 +100,19 @@ class TokenAuthenticationTest < ActionController::IntegrationTest
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
+
test 'should reset token and not authenticate when expire_auth_token_on_timeout is set to true, timeoutable is enabled and we have a timed out session' do
|
104
|
+
swap Devise, :token_authentication_key => :secret_token, :expire_auth_token_on_timeout => true, :timeout_in => (-1).minute do
|
105
|
+
user = sign_in_as_new_user_with_token
|
106
|
+
assert warden.authenticated?(:user)
|
107
|
+
token = user.authentication_token
|
108
|
+
|
109
|
+
get_users_path_as_existing_user(user)
|
110
|
+
assert_not warden.authenticated?(:user)
|
111
|
+
user.reload
|
112
|
+
assert_not_equal token, user.authentication_token
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
103
116
|
test 'should not be subject to injection' do
|
104
117
|
swap Devise, :token_authentication_key => :secret_token do
|
105
118
|
user1 = create_user_with_authentication_token()
|
@@ -14,15 +14,6 @@ class LockableTest < ActiveSupport::TestCase
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
test "should clear failed_attempts on successfull validation" do
|
18
|
-
user = create_user
|
19
|
-
user.confirm!
|
20
|
-
user.valid_for_authentication?{ false }
|
21
|
-
assert_equal 1, user.reload.failed_attempts
|
22
|
-
user.valid_for_authentication?{ true }
|
23
|
-
assert_equal 0, user.reload.failed_attempts
|
24
|
-
end
|
25
|
-
|
26
17
|
test "should increment failed_attempts on successfull validation if the user is already locked" do
|
27
18
|
user = create_user
|
28
19
|
user.confirm!
|
@@ -168,8 +168,7 @@ class RememberableTest < ActiveSupport::TestCase
|
|
168
168
|
|
169
169
|
test 'should have the required_fiels array' do
|
170
170
|
assert_same_content Devise::Models::Rememberable.required_fields(User), [
|
171
|
-
:remember_created_at
|
172
|
-
:remember_token
|
171
|
+
:remember_created_at
|
173
172
|
]
|
174
173
|
end
|
175
174
|
end
|
data/test/models_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class Configurable < User
|
4
|
-
devise :database_authenticatable, :
|
4
|
+
devise :database_authenticatable, :confirmable, :rememberable, :timeoutable, :lockable,
|
5
5
|
:stretches => 15, :pepper => 'abcdef', :allow_unconfirmed_access_for => 5.days,
|
6
6
|
:remember_for => 7.days, :timeout_in => 15.minutes, :unlock_in => 10.days
|
7
7
|
end
|
@@ -39,7 +39,7 @@ class ActiveRecordTest < ActiveSupport::TestCase
|
|
39
39
|
end
|
40
40
|
|
41
41
|
test 'can cherry pick modules' do
|
42
|
-
assert_include_modules Admin, :database_authenticatable, :registerable, :timeoutable, :recoverable, :lockable, :
|
42
|
+
assert_include_modules Admin, :database_authenticatable, :registerable, :timeoutable, :recoverable, :lockable, :confirmable
|
43
43
|
end
|
44
44
|
|
45
45
|
test 'validations options are not applied too late' do
|
@@ -55,12 +55,12 @@ class ActiveRecordTest < ActiveSupport::TestCase
|
|
55
55
|
end
|
56
56
|
|
57
57
|
test 'chosen modules are inheritable' do
|
58
|
-
assert_include_modules Inheritable, :database_authenticatable, :registerable, :timeoutable, :recoverable, :lockable, :
|
58
|
+
assert_include_modules Inheritable, :database_authenticatable, :registerable, :timeoutable, :recoverable, :lockable, :confirmable
|
59
59
|
end
|
60
60
|
|
61
61
|
test 'order of module inclusion' do
|
62
|
-
correct_module_order = [:database_authenticatable, :
|
63
|
-
incorrect_module_order = [:database_authenticatable, :timeoutable, :registerable, :recoverable, :lockable, :
|
62
|
+
correct_module_order = [:database_authenticatable, :recoverable, :registerable, :confirmable, :lockable, :timeoutable]
|
63
|
+
incorrect_module_order = [:database_authenticatable, :timeoutable, :registerable, :recoverable, :lockable, :confirmable]
|
64
64
|
|
65
65
|
assert_include_modules Admin, *incorrect_module_order
|
66
66
|
|