devise 3.1.2 → 3.2.0

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.

Files changed (62) hide show
  1. data/CHANGELOG.md +111 -99
  2. data/Gemfile.lock +1 -1
  3. data/README.md +2 -2
  4. data/app/controllers/devise/confirmations_controller.rb +2 -9
  5. data/app/controllers/devise/passwords_controller.rb +1 -1
  6. data/app/controllers/devise/registrations_controller.rb +6 -6
  7. data/app/controllers/devise/sessions_controller.rb +3 -3
  8. data/app/controllers/devise/unlocks_controller.rb +1 -1
  9. data/app/controllers/devise_controller.rb +6 -2
  10. data/app/mailers/devise/mailer.rb +15 -13
  11. data/config/locales/en.yml +1 -2
  12. data/gemfiles/Gemfile.rails-3.2.x.lock +1 -1
  13. data/lib/devise.rb +23 -12
  14. data/lib/devise/controllers/helpers.rb +16 -84
  15. data/lib/devise/controllers/rememberable.rb +2 -12
  16. data/lib/devise/controllers/sign_in_out.rb +103 -0
  17. data/lib/devise/failure_app.rb +11 -2
  18. data/lib/devise/hooks/forgetable.rb +1 -1
  19. data/lib/devise/hooks/proxy.rb +21 -0
  20. data/lib/devise/hooks/rememberable.rb +1 -1
  21. data/lib/devise/hooks/timeoutable.rb +4 -1
  22. data/lib/devise/models.rb +0 -5
  23. data/lib/devise/models/authenticatable.rb +8 -9
  24. data/lib/devise/models/confirmable.rb +0 -4
  25. data/lib/devise/models/database_authenticatable.rb +17 -7
  26. data/lib/devise/models/lockable.rb +6 -4
  27. data/lib/devise/models/recoverable.rb +0 -8
  28. data/lib/devise/modules.rb +0 -1
  29. data/lib/devise/rails/routes.rb +29 -15
  30. data/lib/devise/strategies/database_authenticatable.rb +3 -6
  31. data/lib/devise/test_helpers.rb +1 -0
  32. data/lib/devise/version.rb +1 -1
  33. data/lib/generators/mongoid/devise_generator.rb +0 -3
  34. data/lib/generators/templates/devise.rb +6 -10
  35. data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
  36. data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
  37. data/lib/generators/templates/markerb/unlock_instructions.markerb +1 -1
  38. data/test/controllers/internal_helpers_test.rb +2 -2
  39. data/test/controllers/sessions_controller_test.rb +1 -1
  40. data/test/devise_test.rb +12 -1
  41. data/test/failure_app_test.rb +11 -0
  42. data/test/integration/confirmable_test.rb +0 -12
  43. data/test/integration/http_authenticatable_test.rb +0 -10
  44. data/test/integration/recoverable_test.rb +2 -2
  45. data/test/integration/rememberable_test.rb +3 -3
  46. data/test/integration/timeoutable_test.rb +28 -0
  47. data/test/mapping_test.rb +2 -2
  48. data/test/models/confirmable_test.rb +0 -9
  49. data/test/models/database_authenticatable_test.rb +19 -1
  50. data/test/models/lockable_test.rb +16 -10
  51. data/test/models/recoverable_test.rb +0 -10
  52. data/test/rails_app/app/mongoid/user.rb +0 -3
  53. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +0 -3
  54. data/test/rails_app/db/schema.rb +0 -1
  55. data/test/rails_app/lib/shared_user.rb +1 -1
  56. data/test/support/locale/en.yml +4 -0
  57. data/test/test_helpers_test.rb +22 -0
  58. metadata +4 -8
  59. data/lib/devise/models/token_authenticatable.rb +0 -92
  60. data/lib/devise/strategies/token_authenticatable.rb +0 -91
  61. data/test/integration/token_authenticatable_test.rb +0 -205
  62. data/test/models/token_authenticatable_test.rb +0 -55
@@ -108,6 +108,7 @@ module Devise
108
108
  Warden::Manager._run_callbacks(:before_failure, env, options)
109
109
 
110
110
  status, headers, response = Devise.warden_config[:failure_app].call(env).to_a
111
+ @controller.response.headers.merge!(headers)
111
112
  @controller.send :render, :status => status, :text => response.body,
112
113
  :content_type => headers["Content-Type"], :location => headers["Location"]
113
114
  nil # causes process return @response
@@ -1,3 +1,3 @@
1
1
  module Devise
2
- VERSION = "3.1.2".freeze
2
+ VERSION = "3.2.0".freeze
3
3
  end
@@ -47,9 +47,6 @@ module Mongoid
47
47
  # field :failed_attempts, :type => Integer, :default => 0 # Only if lock strategy is :failed_attempts
48
48
  # field :unlock_token, :type => String # Only if unlock strategy is :email or :both
49
49
  # field :locked_at, :type => Time
50
-
51
- ## Token authenticatable
52
- # field :authentication_token, :type => String
53
50
  RUBY
54
51
  end
55
52
  end
@@ -56,12 +56,9 @@ Devise.setup do |config|
56
56
 
57
57
  # Tell if authentication through HTTP Auth is enabled. False by default.
58
58
  # It can be set to an array that will enable http authentication only for the
59
- # given strategies, for example, `config.http_authenticatable = [:token]` will
60
- # enable it only for token authentication. The supported strategies are:
59
+ # given strategies, for example, `config.http_authenticatable = [:database]` will
60
+ # enable it only for database authentication. The supported strategies are:
61
61
  # :database = Support basic authentication with authentication key + password
62
- # :token = Support basic authentication with token authentication key
63
- # :token_options = Support token authentication with options as defined in
64
- # http://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html
65
62
  # config.http_authenticatable = false
66
63
 
67
64
  # If http headers should be returned for AJAX requests. True by default.
@@ -76,7 +73,7 @@ Devise.setup do |config|
76
73
  # config.paranoid = true
77
74
 
78
75
  # By default Devise will store the user in session. You can skip storage for
79
- # :http_auth and :token_auth by adding those symbols to the array below.
76
+ # particular strategies by setting this option.
80
77
  # Notice that if you are skipping storage for all authentication paths, you
81
78
  # may want to disable generating routes to Devise's sessions controller by
82
79
  # passing :skip => :sessions to `devise_for` in your config/routes.rb
@@ -176,6 +173,9 @@ Devise.setup do |config|
176
173
  # Time interval to unlock the account if :time is enabled as unlock_strategy.
177
174
  # config.unlock_in = 1.hour
178
175
 
176
+ # Warn on the last attempt before the account is locked.
177
+ # config.last_attempt_warning = false
178
+
179
179
  # ==> Configuration for :recoverable
180
180
  #
181
181
  # Defines which key will be used when recovering the password for an account
@@ -196,10 +196,6 @@ Devise.setup do |config|
196
196
  # Require the `devise-encryptable` gem when using anything other than bcrypt
197
197
  # config.encryptor = :sha512
198
198
 
199
- # ==> Configuration for :token_authenticatable
200
- # Defines name of the authentication token params key
201
- # config.token_authentication_key = :auth_token
202
-
203
199
  # ==> Scopes configuration
204
200
  # Turn scoped views on. Before rendering "sessions/new", it will first check for
205
201
  # "users/sessions/new". It's turned off by default because it's slower if you
@@ -2,4 +2,4 @@ Welcome <%= @email %>!
2
2
 
3
3
  You can confirm your account through the link below:
4
4
 
5
- <%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
5
+ <%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @token) %>
@@ -2,7 +2,7 @@ Hello <%= @resource.email %>!
2
2
 
3
3
  Someone has requested a link to change your password, and you can do this through the link below.
4
4
 
5
- <%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>
5
+ <%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @token) %>
6
6
 
7
7
  If you didn't request this, please ignore this email.
8
8
  Your password won't change until you access the link above and create a new one.
@@ -4,4 +4,4 @@ Your account has been locked due to an excessive number of unsuccessful sign in
4
4
 
5
5
  Click the link below to unlock your account:
6
6
 
7
- <%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>
7
+ <%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @token) %>
@@ -55,7 +55,7 @@ class HelpersTest < ActionController::TestCase
55
55
  end
56
56
 
57
57
  test 'require no authentication tests current mapping' do
58
- @mock_warden.expects(:authenticate?).with(:rememberable, :token_authenticatable, :scope => :user).returns(true)
58
+ @mock_warden.expects(:authenticate?).with(:rememberable, :scope => :user).returns(true)
59
59
  @mock_warden.expects(:user).with(:user).returns(User.new)
60
60
  @controller.expects(:redirect_to).with(root_path)
61
61
  @controller.send :require_no_authentication
@@ -71,7 +71,7 @@ class HelpersTest < ActionController::TestCase
71
71
  end
72
72
 
73
73
  test 'require no authentication sets a flash message' do
74
- @mock_warden.expects(:authenticate?).with(:rememberable, :token_authenticatable, :scope => :user).returns(true)
74
+ @mock_warden.expects(:authenticate?).with(:rememberable, :scope => :user).returns(true)
75
75
  @mock_warden.expects(:user).with(:user).returns(User.new)
76
76
  @controller.expects(:redirect_to).with(root_path)
77
77
  @controller.send :require_no_authentication
@@ -10,7 +10,7 @@ class SessionsControllerTest < ActionController::TestCase
10
10
  end
11
11
  request.env["devise.mapping"] = Devise.mappings[:user]
12
12
  request.session["user_return_to"] = 'foo.bar'
13
- user = create_user
13
+ create_user
14
14
  post :create, :user => {
15
15
  :email => "wrong@email.com",
16
16
  :password => "wrongpassword"
@@ -11,6 +11,17 @@ module Devise
11
11
  end
12
12
 
13
13
  class DeviseTest < ActiveSupport::TestCase
14
+ test 'bcrypt on the class' do
15
+ password = "super secret"
16
+ klass = Struct.new(:pepper, :stretches).new("blahblah", 2)
17
+ hash = Devise.bcrypt(klass, password)
18
+ assert_equal ::BCrypt::Password.create(hash), hash
19
+
20
+ klass = Struct.new(:pepper, :stretches).new("bla", 2)
21
+ hash = Devise.bcrypt(klass, password)
22
+ assert_not_equal ::BCrypt::Password.new(hash), hash
23
+ end
24
+
14
25
  test 'model options can be configured through Devise' do
15
26
  swap Devise, :allow_unconfirmed_access_for => 113, :pepper => "foo" do
16
27
  assert_equal 113, Devise.allow_unconfirmed_access_for
@@ -59,7 +70,7 @@ class DeviseTest < ActiveSupport::TestCase
59
70
  Devise::ALL.delete(:kivi)
60
71
  Devise::CONTROLLERS.delete(:kivi)
61
72
  end
62
-
73
+
63
74
  test 'should complain when comparing empty or different sized passes' do
64
75
  [nil, ""].each do |empty|
65
76
  assert_not Devise.secure_compare(empty, "something")
@@ -8,6 +8,12 @@ class FailureTest < ActiveSupport::TestCase
8
8
  end
9
9
  end
10
10
 
11
+ class FailureWithI18nOptions < Devise::FailureApp
12
+ def i18n_options(options)
13
+ options.merge(:name => 'Steve')
14
+ end
15
+ end
16
+
11
17
  def self.context(name, &block)
12
18
  instance_eval(&block)
13
19
  end
@@ -67,6 +73,11 @@ class FailureTest < ActiveSupport::TestCase
67
73
  assert_equal 'http://test.host/users/sign_in', @response.second["Location"]
68
74
  end
69
75
 
76
+ test 'uses custom i18n options' do
77
+ call_failure('warden' => OpenStruct.new(:message => :does_not_exist), :app => FailureWithI18nOptions)
78
+ assert_equal 'User Steve does not exist', @request.flash[:alert]
79
+ end
80
+
70
81
  test 'uses the proxy failure message as string' do
71
82
  call_failure('warden' => OpenStruct.new(:message => 'Hello world'))
72
83
  assert_equal 'Hello world', @request.flash[:alert]
@@ -62,18 +62,6 @@ class ConfirmationTest < ActionDispatch::IntegrationTest
62
62
  end
63
63
  end
64
64
 
65
- test 'user should be signed in after confirmation if allow_insecure_sign_in_after_confirmation is enabled' do
66
- swap Devise, :confirm_within => 3.days, :allow_insecure_sign_in_after_confirmation => true do
67
- user = create_user(:confirm => false, :confirmation_sent_at => 2.days.ago)
68
- assert_not user.confirmed?
69
- visit_user_confirmation_with_token(user.raw_confirmation_token)
70
-
71
- assert_contain 'Your account was successfully confirmed. You are now signed in.'
72
- assert_current_url root_url
73
- assert user.reload.confirmed?
74
- end
75
- end
76
-
77
65
  test 'user should be redirected to a custom path after confirmation' do
78
66
  Devise::ConfirmationsController.any_instance.stubs(:after_confirmation_path_for).returns("/?custom=1")
79
67
 
@@ -88,16 +88,6 @@ class HttpAuthenticationTest < ActionDispatch::IntegrationTest
88
88
  end
89
89
  end
90
90
 
91
- test 'sign in should authenticate with really long token' do
92
- token = "token_containing_so_many_characters_that_the_base64_encoding_will_wrap"
93
- user = create_user
94
- user.update_attribute :authentication_token, token
95
- get users_path(:format => :xml), {}, "HTTP_AUTHORIZATION" => "Basic #{Base64.encode64("#{token}:x")}"
96
- assert_response :success
97
- assert_match "<email>user@test.com</email>", response.body
98
- assert warden.authenticated?(:user)
99
- end
100
-
101
91
  private
102
92
 
103
93
  def sign_in_as_new_user_with_http(username="user@test.com", password="12345678")
@@ -190,7 +190,7 @@ class PasswordTest < ActionDispatch::IntegrationTest
190
190
  end
191
191
 
192
192
  test 'sign in user automatically after changing its password' do
193
- user = create_user
193
+ create_user
194
194
  request_forgot_password
195
195
  reset_password
196
196
 
@@ -260,7 +260,7 @@ class PasswordTest < ActionDispatch::IntegrationTest
260
260
  end
261
261
 
262
262
  test 'change password with valid parameters in XML format should return valid response' do
263
- user = create_user
263
+ create_user
264
264
  request_forgot_password
265
265
  put user_password_path(:format => 'xml'), :user => {
266
266
  :reset_password_token => 'abcdef', :password => '987654321', :password_confirmation => '987654321'
@@ -64,14 +64,14 @@ class RememberMeTest < ActionDispatch::IntegrationTest
64
64
  # since we changed the domain. This is the only difference with the
65
65
  # previous test.
66
66
  swap Devise, :rememberable_options => { :domain => "omg.somewhere.com" } do
67
- user = sign_in_as_user :remember_me => true
67
+ sign_in_as_user :remember_me => true
68
68
  assert_nil request.cookies["remember_user_token"]
69
69
  end
70
70
  end
71
71
 
72
72
  test 'generate remember token with a custom key' do
73
73
  swap Devise, :rememberable_options => { :key => "v1lat_token" } do
74
- user = sign_in_as_user :remember_me => true
74
+ sign_in_as_user :remember_me => true
75
75
  assert request.cookies["v1lat_token"]
76
76
  end
77
77
  end
@@ -79,7 +79,7 @@ class RememberMeTest < ActionDispatch::IntegrationTest
79
79
  test 'generate remember token after sign in setting session options' do
80
80
  begin
81
81
  Rails.configuration.session_options[:domain] = "omg.somewhere.com"
82
- user = sign_in_as_user :remember_me => true
82
+ sign_in_as_user :remember_me => true
83
83
  assert_nil request.cookies["remember_user_token"]
84
84
  ensure
85
85
  Rails.configuration.session_options.delete(:domain)
@@ -45,6 +45,20 @@ class SessionTimeoutTest < ActionDispatch::IntegrationTest
45
45
  assert_not warden.authenticated?(:user)
46
46
  end
47
47
 
48
+ test 'time out all sessions after default limit time when sign_out_all_scopes is true' do
49
+ swap Devise, sign_out_all_scopes: true do
50
+ sign_in_as_admin
51
+
52
+ user = sign_in_as_user
53
+ get expire_user_path(user)
54
+ assert_not_nil last_request_at
55
+
56
+ get root_path
57
+ assert_not warden.authenticated?(:user)
58
+ assert_not warden.authenticated?(:admin)
59
+ end
60
+ end
61
+
48
62
  test 'time out user session after deault limit time and redirect to latest get request' do
49
63
  user = sign_in_as_user
50
64
  visit edit_form_user_path(user)
@@ -67,6 +81,20 @@ class SessionTimeoutTest < ActionDispatch::IntegrationTest
67
81
  assert_contain 'Signed out successfully'
68
82
  end
69
83
 
84
+ test 'expired session is not extended by sign in page' do
85
+ user = sign_in_as_user
86
+ get expire_user_path(user)
87
+ assert warden.authenticated?(:user)
88
+
89
+ get "/users/sign_in"
90
+ assert_redirected_to "/users/sign_in"
91
+ follow_redirect!
92
+
93
+ assert_response :success
94
+ assert_contain 'Sign in'
95
+ assert_not warden.authenticated?(:user)
96
+ end
97
+
70
98
  test 'time out is not triggered on sign in' do
71
99
  user = sign_in_as_user
72
100
  get expire_user_path(user)
@@ -50,12 +50,12 @@ class MappingTest < ActiveSupport::TestCase
50
50
  end
51
51
 
52
52
  test 'has strategies depending on the model declaration' do
53
- assert_equal [:rememberable, :token_authenticatable, :database_authenticatable], Devise.mappings[:user].strategies
53
+ assert_equal [:rememberable, :database_authenticatable], Devise.mappings[:user].strategies
54
54
  assert_equal [:database_authenticatable], Devise.mappings[:admin].strategies
55
55
  end
56
56
 
57
57
  test 'has no input strategies depending on the model declaration' do
58
- assert_equal [:rememberable, :token_authenticatable], Devise.mappings[:user].no_input_strategies
58
+ assert_equal [:rememberable], Devise.mappings[:user].no_input_strategies
59
59
  assert_equal [], Devise.mappings[:admin].no_input_strategies
60
60
  end
61
61
 
@@ -51,15 +51,6 @@ class ConfirmableTest < ActiveSupport::TestCase
51
51
  assert_equal "was already confirmed, please try signing in", user.errors[:email].join
52
52
  end
53
53
 
54
- test 'DEPRECATED: should find and confirm a user automatically' do
55
- swap Devise, allow_insecure_token_lookup: true do
56
- user = create_user
57
- confirmed_user = User.confirm_by_token(user.confirmation_token)
58
- assert_equal confirmed_user, user
59
- assert user.reload.confirmed?
60
- end
61
- end
62
-
63
54
  test 'should find and confirm a user automatically based on the raw token' do
64
55
  user = create_user
65
56
  raw = user.raw_confirmation_token
@@ -24,6 +24,15 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
24
24
  assert_equal confirmation.downcase, user.email_confirmation
25
25
  end
26
26
 
27
+ test 'should not mutate value assigned to case insensitive key' do
28
+ email = 'Foo@Bar.com'
29
+ original_email = email.dup
30
+ user = new_user(:email => email)
31
+
32
+ user.save!
33
+ assert_equal original_email, email
34
+ end
35
+
27
36
  test 'should remove whitespace from strip whitespace keys when saving' do
28
37
  # strip_whitespace_keys is set to :email by default.
29
38
  email = ' foo@bar.com '
@@ -34,6 +43,15 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
34
43
  assert_equal email.strip, user.email
35
44
  end
36
45
 
46
+ test 'should not mutate value assigned to string whitespace key' do
47
+ email = ' foo@bar.com '
48
+ original_email = email.dup
49
+ user = new_user(:email => email)
50
+
51
+ user.save!
52
+ assert_equal original_email, email
53
+ end
54
+
37
55
  test "doesn't throw exception when globally configured strip_whitespace_keys are not present on a model" do
38
56
  swap Devise, :strip_whitespace_keys => [:fake_key] do
39
57
  assert_nothing_raised { create_user }
@@ -203,7 +221,7 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
203
221
  end
204
222
 
205
223
  test 'downcase_keys with validation' do
206
- user = User.create(:email => "HEllO@example.com", :password => "123456")
224
+ User.create(:email => "HEllO@example.com", :password => "123456")
207
225
  user = User.create(:email => "HEllO@example.com", :password => "123456")
208
226
  assert !user.valid?
209
227
  end
@@ -139,16 +139,6 @@ class LockableTest < ActiveSupport::TestCase
139
139
  end
140
140
  end
141
141
 
142
- test 'DEPRECATED: should find and unlock a user automatically' do
143
- swap Devise, allow_insecure_token_lookup: true do
144
- user = create_user
145
- user.lock_access!
146
- locked_user = User.unlock_access_by_token(user.unlock_token)
147
- assert_equal locked_user, user
148
- assert_not user.reload.access_locked?
149
- end
150
- end
151
-
152
142
  test 'should find and unlock a user automatically based on raw token' do
153
143
  user = create_user
154
144
  raw = user.send_unlock_instructions
@@ -289,4 +279,20 @@ class LockableTest < ActiveSupport::TestCase
289
279
  assert_equal :invalid, user.unauthenticated_message
290
280
  end
291
281
  end
282
+
283
+ test 'should return last attempt message if user made next-to-last attempt of password entering' do
284
+ swap Devise, :last_attempt_warning => :true do
285
+ swap Devise, :lock_strategy => :failed_attempts do
286
+ user = create_user
287
+ user.failed_attempts = Devise.maximum_attempts - 1
288
+ assert_equal :invalid, user.unauthenticated_message
289
+
290
+ user.failed_attempts = Devise.maximum_attempts
291
+ assert_equal :last_attempt, user.unauthenticated_message
292
+
293
+ user.failed_attempts = Devise.maximum_attempts + 1
294
+ assert_equal :locked, user.unauthenticated_message
295
+ end
296
+ end
297
+ end
292
298
  end
@@ -108,16 +108,6 @@ class RecoverableTest < ActiveSupport::TestCase
108
108
  end
109
109
  end
110
110
 
111
- test 'DEPRECATED: should find a user to reset his password based on reset_password_token' do
112
- swap Devise, allow_insecure_token_lookup: true do
113
- user = create_user
114
- user.send_reset_password_instructions
115
-
116
- reset_password_user = User.reset_password_by_token(:reset_password_token => user.reset_password_token)
117
- assert_equal reset_password_user, user
118
- end
119
- end
120
-
121
111
  test 'should find a user to reset his password based on the raw token' do
122
112
  user = create_user
123
113
  raw = user.send_reset_password_instructions
@@ -36,7 +36,4 @@ class User
36
36
  field :failed_attempts, :type => Integer, :default => 0 # Only if lock strategy is :failed_attempts
37
37
  field :unlock_token, :type => String # Only if unlock strategy is :email or :both
38
38
  field :locked_at, :type => Time
39
-
40
- ## Token authenticatable
41
- field :authentication_token, :type => String
42
39
  end
@@ -33,9 +33,6 @@ class CreateTables < ActiveRecord::Migration
33
33
  t.string :unlock_token # Only if unlock strategy is :email or :both
34
34
  t.datetime :locked_at
35
35
 
36
- ## Token authenticatable
37
- t.string :authentication_token
38
-
39
36
  t.timestamps
40
37
  end
41
38