devise 1.3.4 → 1.4.1

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 (74) hide show
  1. data/.travis.yml +3 -2
  2. data/CHANGELOG.rdoc +29 -0
  3. data/Gemfile +10 -7
  4. data/Gemfile.lock +47 -47
  5. data/README.rdoc +2 -2
  6. data/Rakefile +1 -1
  7. data/app/controllers/devise/confirmations_controller.rb +2 -2
  8. data/app/controllers/devise/passwords_controller.rb +10 -2
  9. data/app/controllers/devise/registrations_controller.rb +6 -4
  10. data/app/controllers/devise/unlocks_controller.rb +2 -2
  11. data/app/helpers/devise_helper.rb +1 -1
  12. data/app/mailers/devise/mailer.rb +4 -77
  13. data/config/locales/en.yml +3 -0
  14. data/lib/devise.rb +45 -13
  15. data/lib/devise/controllers/helpers.rb +5 -2
  16. data/lib/devise/controllers/internal_helpers.rb +15 -1
  17. data/lib/devise/controllers/rememberable.rb +1 -1
  18. data/lib/devise/email.rb +23 -0
  19. data/lib/devise/hooks/forgetable.rb +1 -1
  20. data/lib/devise/hooks/trackable.rb +1 -1
  21. data/lib/devise/mailers/helpers.rb +84 -0
  22. data/lib/devise/mapping.rb +23 -7
  23. data/lib/devise/models/authenticatable.rb +14 -6
  24. data/lib/devise/models/database_authenticatable.rb +18 -1
  25. data/lib/devise/models/recoverable.rb +1 -1
  26. data/lib/devise/models/rememberable.rb +7 -5
  27. data/lib/devise/models/validatable.rb +5 -7
  28. data/lib/devise/modules.rb +1 -1
  29. data/lib/devise/omniauth.rb +0 -5
  30. data/lib/devise/omniauth/config.rb +6 -0
  31. data/lib/devise/rails/routes.rb +65 -10
  32. data/lib/devise/strategies/rememberable.rb +2 -7
  33. data/lib/devise/version.rb +1 -1
  34. data/lib/generators/devise/install_generator.rb +2 -2
  35. data/lib/generators/devise/simple_form_for/confirmations/new.html.erb +15 -0
  36. data/lib/generators/devise/simple_form_for/passwords/edit.html.erb +19 -0
  37. data/lib/generators/devise/simple_form_for/passwords/new.html.erb +15 -0
  38. data/lib/generators/devise/simple_form_for/registrations/edit.html.erb +22 -0
  39. data/lib/generators/devise/simple_form_for/registrations/new.html.erb +17 -0
  40. data/lib/generators/devise/simple_form_for/sessions/new.html.erb +15 -0
  41. data/lib/generators/devise/simple_form_for/unlocks/new.html.erb +15 -0
  42. data/lib/generators/devise/views_generator.rb +61 -9
  43. data/lib/generators/templates/devise.rb +13 -3
  44. data/test/controllers/internal_helpers_test.rb +9 -2
  45. data/test/generators/views_generator_test.rb +10 -0
  46. data/test/helpers/devise_helper_test.rb +43 -0
  47. data/test/integration/authenticatable_test.rb +74 -5
  48. data/test/integration/confirmable_test.rb +39 -1
  49. data/test/integration/database_authenticatable_test.rb +22 -0
  50. data/test/integration/http_authenticatable_test.rb +8 -0
  51. data/test/integration/lockable_test.rb +62 -4
  52. data/test/integration/omniauthable_test.rb +1 -3
  53. data/test/integration/recoverable_test.rb +66 -6
  54. data/test/integration/registerable_test.rb +1 -1
  55. data/test/integration/rememberable_test.rb +20 -1
  56. data/test/integration/trackable_test.rb +17 -0
  57. data/test/mapping_test.rb +5 -0
  58. data/test/models/database_authenticatable_test.rb +56 -1
  59. data/test/models/encryptable_test.rb +1 -1
  60. data/test/models/recoverable_test.rb +14 -3
  61. data/test/models/rememberable_test.rb +8 -0
  62. data/test/models/token_authenticatable_test.rb +0 -6
  63. data/test/models/validatable_test.rb +17 -4
  64. data/test/models_test.rb +4 -0
  65. data/test/omniauth/url_helpers_test.rb +4 -0
  66. data/test/rails_app/app/controllers/home_controller.rb +9 -0
  67. data/test/rails_app/app/controllers/users_controller.rb +6 -1
  68. data/test/rails_app/app/views/home/admin_dashboard.html.erb +1 -0
  69. data/test/rails_app/app/views/home/join.html.erb +1 -0
  70. data/test/rails_app/app/views/home/user_dashboard.html.erb +1 -0
  71. data/test/rails_app/config/initializers/devise.rb +6 -0
  72. data/test/rails_app/config/routes.rb +30 -2
  73. data/test/routes_test.rb +54 -0
  74. metadata +21 -4
@@ -17,7 +17,7 @@ class PasswordTest < ActionController::IntegrationTest
17
17
  click_button 'Send me reset password instructions'
18
18
  end
19
19
 
20
- def reset_password(options={}, &block)
20
+ def reset_password(options={}, &block)
21
21
  visit edit_user_password_path(:reset_password_token => options[:reset_password_token]) unless options[:visit] == false
22
22
  assert_response :success
23
23
 
@@ -29,11 +29,11 @@ class PasswordTest < ActionController::IntegrationTest
29
29
 
30
30
  test 'reset password with email of different case should succeed when email is in the list of case insensitive keys' do
31
31
  create_user(:email => 'Foo@Bar.com')
32
-
32
+
33
33
  request_forgot_password do
34
34
  fill_in 'email', :with => 'foo@bar.com'
35
35
  end
36
-
36
+
37
37
  assert_current_url '/users/sign_in'
38
38
  assert_contain 'You will receive an email with instructions about how to reset your password in a few minutes.'
39
39
  end
@@ -41,11 +41,11 @@ class PasswordTest < ActionController::IntegrationTest
41
41
  test 'reset password with email of different case should fail when email is NOT the list of case insensitive keys' do
42
42
  swap Devise, :case_insensitive_keys => [] do
43
43
  create_user(:email => 'Foo@Bar.com')
44
-
44
+
45
45
  request_forgot_password do
46
46
  fill_in 'email', :with => 'foo@bar.com'
47
47
  end
48
-
48
+
49
49
  assert_response :success
50
50
  assert_current_url '/users/password'
51
51
  assert_have_selector "input[type=email][value='foo@bar.com']"
@@ -53,6 +53,32 @@ class PasswordTest < ActionController::IntegrationTest
53
53
  end
54
54
  end
55
55
 
56
+ test 'reset password with email with extra whitespace should succeed when email is in the list of strip whitespace keys' do
57
+ create_user(:email => 'foo@bar.com')
58
+
59
+ request_forgot_password do
60
+ fill_in 'email', :with => ' foo@bar.com '
61
+ end
62
+
63
+ assert_current_url '/users/sign_in'
64
+ assert_contain 'You will receive an email with instructions about how to reset your password in a few minutes.'
65
+ end
66
+
67
+ test 'reset password with email with extra whitespace should fail when email is NOT the list of strip whitespace keys' do
68
+ swap Devise, :strip_whitespace_keys => [] do
69
+ create_user(:email => 'foo@bar.com')
70
+
71
+ request_forgot_password do
72
+ fill_in 'email', :with => ' foo@bar.com '
73
+ end
74
+
75
+ assert_response :success
76
+ assert_current_url '/users/password'
77
+ assert_have_selector "input[type=email][value=' foo@bar.com ']"
78
+ assert_contain 'not found'
79
+ end
80
+ end
81
+
56
82
  test 'authenticated user should not be able to visit forgot password page' do
57
83
  sign_in_as_user
58
84
  assert warden.authenticated?(:user)
@@ -161,7 +187,7 @@ class PasswordTest < ActionController::IntegrationTest
161
187
  create_user
162
188
  post user_password_path(:format => 'xml'), :user => {:email => "user@test.com"}
163
189
  assert_response :success
164
- assert response.body.include? %(<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<user>)
190
+ assert_equal response.body, { }.to_xml
165
191
  end
166
192
 
167
193
  test 'reset password request with invalid E-Mail in XML format should return valid response' do
@@ -194,4 +220,38 @@ class PasswordTest < ActionController::IntegrationTest
194
220
  assert_response :unprocessable_entity
195
221
  assert response.body.include? %(<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<errors>)
196
222
  end
223
+
224
+ test "when using json requests to ask a confirmable request, should not return the object" do
225
+ user = create_user(:confirm => false)
226
+
227
+ post user_password_path(:format => :json), :user => { :email => user.email }
228
+
229
+ assert_response :success
230
+ assert_equal response.body, "{}"
231
+ end
232
+
233
+ test "when in paranoid mode and with an invalid e-mail, asking to reset a password should display a message that does not indicates that the e-mail does not exists in the database" do
234
+ swap Devise, :paranoid => true do
235
+ visit_new_password_path
236
+ fill_in "email", :with => "arandomemail@test.com"
237
+ click_button 'Send me reset password instructions'
238
+
239
+ assert_not_contain "1 error prohibited this user from being saved:"
240
+ assert_not_contain "Email not found"
241
+ assert_contain "If your e-mail exists on our database, you will receive a password recovery link on your e-mail"
242
+ assert_current_url "/users/password"
243
+ end
244
+ end
245
+
246
+ test "when in paranoid mode and with a valid e-mail, asking to reset password should display a message that does not indicates that the email exists in the database and redirect to the failure route" do
247
+ swap Devise, :paranoid => true do
248
+ user = create_user
249
+ visit_new_password_path
250
+ fill_in 'email', :with => user.email
251
+ click_button 'Send me reset password instructions'
252
+
253
+ assert_contain "If your e-mail exists on our database, you will receive a password recovery link on your e-mail"
254
+ assert_current_url "/users/password"
255
+ end
256
+ end
197
257
  end
@@ -211,7 +211,7 @@ class RegistrationTest < ActionController::IntegrationTest
211
211
  get new_user_registration_path(:format => 'xml')
212
212
  assert_response :success
213
213
  assert_match %(<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<user>), response.body
214
- assert_no_match(/<confirmation_token>/, response.body) if DEVISE_ORM == :active_record
214
+ assert_no_match(/<confirmation-token/, response.body) if DEVISE_ORM == :active_record
215
215
  end
216
216
 
217
217
  test 'a user with JSON sign up stub' do
@@ -38,6 +38,18 @@ class RememberMeTest < ActionController::IntegrationTest
38
38
  assert_nil request.cookies["remember_user_cookie"]
39
39
  end
40
40
 
41
+ test 'handles unverified requests gets rid of caches' do
42
+ swap UsersController, :allow_forgery_protection => true do
43
+ post exhibit_user_url(1)
44
+ assert_not warden.authenticated?(:user)
45
+
46
+ create_user_and_remember
47
+ post exhibit_user_url(1)
48
+ assert_equal "User is not authenticated", response.body
49
+ assert_not warden.authenticated?(:user)
50
+ end
51
+ end
52
+
41
53
  test 'generate remember token after sign in' do
42
54
  user = sign_in_as_user :remember_me => true
43
55
  assert request.cookies["remember_user_token"]
@@ -69,7 +81,14 @@ class RememberMeTest < ActionController::IntegrationTest
69
81
  assert_response :success
70
82
  assert warden.authenticated?(:user)
71
83
  assert warden.user(:user) == user
72
- assert_match /remember_user_token[^\n]*HttpOnly\n/, response.headers["Set-Cookie"], "Expected Set-Cookie header in response to set HttpOnly flag on remember_user_token cookie."
84
+ assert_match /remember_user_token[^\n]*HttpOnly/, response.headers["Set-Cookie"], "Expected Set-Cookie header in response to set HttpOnly flag on remember_user_token cookie."
85
+ end
86
+
87
+ test 'remember the user before sign up and redirect him to his home' do
88
+ user = create_user_and_remember
89
+ get new_user_registration_path
90
+ assert warden.authenticated?(:user)
91
+ assert_redirected_to root_path
73
92
  end
74
93
 
75
94
  test 'cookies are destroyed on unverified requests' do
@@ -61,4 +61,21 @@ class TrackableHooksTest < ActionController::IntegrationTest
61
61
  assert_nil user.last_sign_in_at
62
62
  end
63
63
  end
64
+
65
+ test "do not track if devise.skip_trackable is set" do
66
+ user = create_user
67
+ sign_in_as_user do
68
+ header 'devise.skip_trackable', '1'
69
+ end
70
+ user.reload
71
+ assert_equal 0, user.sign_in_count
72
+ visit destroy_user_session_path
73
+
74
+ sign_in_as_user do
75
+ header 'devise.skip_trackable', false
76
+ end
77
+ user.reload
78
+ assert_equal 1, user.sign_in_count
79
+ end
80
+
64
81
  end
data/test/mapping_test.rb CHANGED
@@ -50,6 +50,11 @@ class MappingTest < ActiveSupport::TestCase
50
50
  assert_equal [:rememberable, :database_authenticatable], Devise.mappings[:admin].strategies
51
51
  end
52
52
 
53
+ test 'has no input strategies depending on the model declaration' do
54
+ assert_equal [:rememberable, :token_authenticatable], Devise.mappings[:user].no_input_strategies
55
+ assert_equal [:rememberable], Devise.mappings[:admin].no_input_strategies
56
+ end
57
+
53
58
  test 'find scope for a given object' do
54
59
  assert_equal :user, Devise::Mapping.find_scope!(User)
55
60
  assert_equal :user, Devise::Mapping.find_scope!(:user)
@@ -11,6 +11,39 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
11
11
  user.save!
12
12
  assert_equal email.downcase, user.email
13
13
  end
14
+
15
+ test 'should remove whitespace from strip whitespace keys when saving' do
16
+ # strip_whitespace_keys is set to :email by default.
17
+ email = ' foo@bar.com '
18
+ user = new_user(:email => email)
19
+
20
+ assert_equal email, user.email
21
+ user.save!
22
+ assert_equal email.strip, user.email
23
+ end
24
+
25
+ test 'find_for_authentication and filter_auth_params should not modify the conditions hash' do
26
+ FilterAuthUser = Class.new(User) do
27
+ def self.filter_auth_params(conditions)
28
+ if conditions.is_a?(Hash) && login = conditions.delete('login')
29
+ key = login.include?('@') ? :email : :username
30
+ conditions[key] = login
31
+ end
32
+ super(conditions)
33
+ end
34
+ end
35
+
36
+ conditions = { 'login' => 'foo@bar.com' }
37
+ FilterAuthUser.find_for_authentication(conditions)
38
+
39
+ assert_equal({ 'login' => 'foo@bar.com' }, conditions)
40
+ end
41
+
42
+ test "filter_auth_params should not convert booleans and integer to strings" do
43
+ conditions = { 'login' => 'foo@bar.com', "bool1" => true, "bool2" => false, "fixnum" => 123, "will_be_converted" => (1..10) }
44
+ conditions = User.__send__(:filter_auth_params, conditions)
45
+ assert_equal( { 'login' => 'foo@bar.com', "bool1" => true, "bool2" => false, "fixnum" => 123, "will_be_converted" => "1..10" }, conditions)
46
+ end
14
47
 
15
48
  test 'should respond to password and password confirmation' do
16
49
  user = new_user
@@ -70,7 +103,7 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
70
103
  :password => 'pass321', :password_confirmation => 'pass321')
71
104
  assert user.reload.valid_password?('pass321')
72
105
  end
73
-
106
+
74
107
  test 'should add an error to current password when it is invalid' do
75
108
  user = create_user
76
109
  assert_not user.update_with_password(:current_password => 'other',
@@ -87,6 +120,15 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
87
120
  assert_match "can't be blank", user.errors[:current_password].join
88
121
  end
89
122
 
123
+ test 'should run validations even when current password is invalid or blank' do
124
+ user = UserWithValidation.create!(valid_attributes)
125
+ user.save
126
+ assert user.persisted?
127
+ assert_not user.update_with_password(:username => "")
128
+ assert_match "usertest", user.reload.username
129
+ assert_match "can't be blank", user.errors[:username].join
130
+ end
131
+
90
132
  test 'should ignore password and its confirmation if they are blank' do
91
133
  user = create_user
92
134
  assert user.update_with_password(:current_password => '123456', :email => "new@example.com")
@@ -108,6 +150,19 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
108
150
  assert user.password_confirmation.blank?
109
151
  end
110
152
 
153
+ test 'should update the user without password' do
154
+ user = create_user
155
+ user.update_without_password(:email => 'new@example.com')
156
+ assert_equal 'new@example.com', user.email
157
+ end
158
+
159
+ test 'should not update password without password' do
160
+ user = create_user
161
+ user.update_without_password(:password => 'pass321', :password_confirmation => 'pass321')
162
+ assert !user.reload.valid_password?('pass321')
163
+ assert user.valid_password?('123456')
164
+ end
165
+
111
166
  test 'downcase_keys with validation' do
112
167
  user = User.create(:email => "HEllO@example.com", :password => "123456")
113
168
  user = User.create(:email => "HEllO@example.com", :password => "123456")
@@ -31,7 +31,7 @@ class EncryptableTest < ActiveSupport::TestCase
31
31
 
32
32
  test 'should generate a base64 hash using SecureRandom for password salt' do
33
33
  swap_with_encryptor Admin, :sha1 do
34
- ActiveSupport::SecureRandom.expects(:base64).with(15).returns('friendly_token')
34
+ SecureRandom.expects(:base64).with(15).returns('friendly_token')
35
35
  assert_equal 'friendly_token', create_admin.password_salt
36
36
  end
37
37
  end
@@ -198,8 +198,13 @@ class RecoverableTest < ActiveSupport::TestCase
198
198
 
199
199
  test 'should save the model when the reset_password_sent_at doesnt exist' do
200
200
  user = create_user
201
- user.stubs(:respond_to?).with(:reset_password_sent_at=).returns(false)
202
- user.stubs(:respond_to?).with(:headers_for).returns(false)
201
+ def user.respond_to?(meth, *)
202
+ if meth == :reset_password_sent_at=
203
+ false
204
+ else
205
+ super
206
+ end
207
+ end
203
208
  user.send_reset_password_instructions
204
209
  user.reload
205
210
  assert_not_nil user.reset_password_token
@@ -207,7 +212,13 @@ class RecoverableTest < ActiveSupport::TestCase
207
212
 
208
213
  test 'should have valid period if does not respond to reset_password_sent_at' do
209
214
  user = create_user
210
- user.stubs(:respond_to?).with(:reset_password_sent_at).returns(false)
215
+ def user.respond_to?(meth, *)
216
+ if meth == :reset_password_sent_at
217
+ false
218
+ else
219
+ super
220
+ end
221
+ end
211
222
  assert user.reset_password_period_valid?
212
223
  end
213
224
 
@@ -15,6 +15,14 @@ module SharedRememberableTest
15
15
  resource.forget_me!
16
16
  assert resource.remember_created_at.nil?
17
17
  end
18
+
19
+ test 'forget_me should not try to update resource if it has been destroyed' do
20
+ resource = create_resource
21
+ resource.destroy
22
+ resource.expects(:remember_created_at).never
23
+ resource.expects(:save).never
24
+ resource.forget_me!
25
+ end
18
26
 
19
27
  test 'remember is expired if not created at timestamp is set' do
20
28
  assert create_resource.remember_expired?
@@ -27,12 +27,6 @@ class TokenAuthenticatableTest < ActiveSupport::TestCase
27
27
  end
28
28
 
29
29
  test 'should return nil when authenticating an invalid user by authentication token' do
30
- if DEVISE_ORM == :mongoid
31
- raise 'There is an incompatibility between Devise and Mongoid' <<
32
- ' that makes this test break. For more information, check' <<
33
- ' this issue: https://github.com/mongoid/mongoid/issues/725'
34
- end
35
-
36
30
  user = create_user
37
31
  user.ensure_authentication_token!
38
32
  user.confirm!
@@ -8,7 +8,7 @@ class ValidatableTest < ActiveSupport::TestCase
8
8
  assert_equal 'can\'t be blank', user.errors[:email].join
9
9
  end
10
10
 
11
- test 'should require uniqueness of email, allowing blank' do
11
+ test 'should require uniqueness of email if email has changed, allowing blank' do
12
12
  existing_user = create_user
13
13
 
14
14
  user = new_user(:email => '')
@@ -18,18 +18,24 @@ class ValidatableTest < ActiveSupport::TestCase
18
18
  user.email = existing_user.email
19
19
  assert user.invalid?
20
20
  assert_match(/taken/, user.errors[:email].join)
21
+
22
+ user.save(:validate => false)
23
+ assert user.valid?
21
24
  end
22
25
 
23
- test 'should require correct email format, allowing blank' do
26
+ test 'should require correct email format if email has changed, allowing blank' do
24
27
  user = new_user(:email => '')
25
28
  assert user.invalid?
26
29
  assert_not_equal 'is invalid', user.errors[:email].join
27
30
 
28
- %w(invalid_email_format email@invalid invalid$character@mail.com other@not 123).each do |email|
31
+ %w(invalid_email_format 123 $$$ \(\) ).each do |email|
29
32
  user.email = email
30
33
  assert user.invalid?, 'should be invalid with email ' << email
31
34
  assert_equal 'is invalid', user.errors[:email].join
32
35
  end
36
+
37
+ user.save(:validate => false)
38
+ assert user.valid?
33
39
  end
34
40
 
35
41
  test 'should accept valid emails' do
@@ -85,12 +91,19 @@ class ValidatableTest < ActiveSupport::TestCase
85
91
  user = create_user.reload
86
92
  user.password = user.password_confirmation = nil
87
93
  assert user.valid?
88
-
94
+
89
95
  user.password_confirmation = 'confirmation'
90
96
  assert user.invalid?
91
97
  assert_not (user.errors[:password].join =~ /is too long/)
92
98
  end
93
99
 
100
+ test 'should complain about length even if possword is not required' do
101
+ user = new_user(:password => 'x'*129, :password_confirmation => 'x'*129)
102
+ user.stubs(:password_required?).returns(false)
103
+ assert user.invalid?
104
+ assert_equal 'is too long (maximum is 128 characters)', user.errors[:password].join
105
+ end
106
+
94
107
  test 'shuold not be included in objects with invalid API' do
95
108
  assert_raise RuntimeError do
96
109
  Class.new.send :include, Devise::Models::Validatable
data/test/models_test.rb CHANGED
@@ -10,6 +10,10 @@ class WithValidation < Admin
10
10
  devise :database_authenticatable, :validatable, :password_length => 2..6
11
11
  end
12
12
 
13
+ class UserWithValidation < User
14
+ validates_presence_of :username
15
+ end
16
+
13
17
  class Several < Admin
14
18
  devise :validatable
15
19
  devise :lockable
@@ -35,6 +35,10 @@ class OmniAuthRoutesTest < ActionController::TestCase
35
35
  end
36
36
  end
37
37
 
38
+ test 'should generate authorization path for named open_id omniauth' do
39
+ assert_match "/users/auth/google", @controller.omniauth_authorize_path(:user, :google)
40
+ end
41
+
38
42
  test 'should generate authorization path with params' do
39
43
  assert_match "/users/auth/open_id?openid_url=http%3A%2F%2Fyahoo.com",
40
44
  @controller.omniauth_authorize_path(:user, :open_id, :openid_url => "http://yahoo.com")
@@ -5,6 +5,15 @@ class HomeController < ApplicationController
5
5
  def private
6
6
  end
7
7
 
8
+ def user_dashboard
9
+ end
10
+
11
+ def admin_dashboard
12
+ end
13
+
14
+ def join
15
+ end
16
+
8
17
  def set
9
18
  session["devise.foo_bar"] = "something"
10
19
  head :ok