devise 3.5.2 → 3.5.3
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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -3
- data/CHANGELOG.md +17 -1
- data/CODE_OF_CONDUCT.md +22 -0
- data/CONTRIBUTING.md +2 -0
- data/Gemfile.lock +2 -2
- data/app/controllers/devise/passwords_controller.rb +1 -0
- data/app/mailers/devise/mailer.rb +4 -0
- data/app/views/devise/mailer/password_change.html.erb +3 -0
- data/app/views/devise/shared/_links.html.erb +1 -1
- data/config/locales/en.yml +2 -0
- data/gemfiles/Gemfile.rails-3.2-stable.lock +5 -2
- data/gemfiles/Gemfile.rails-4.0-stable.lock +5 -2
- data/gemfiles/Gemfile.rails-4.1-stable.lock +5 -2
- data/gemfiles/Gemfile.rails-4.2-stable.lock +5 -2
- data/lib/devise.rb +12 -3
- data/lib/devise/controllers/helpers.rb +12 -6
- data/lib/devise/failure_app.rb +17 -3
- data/lib/devise/hooks/timeoutable.rb +2 -1
- data/lib/devise/models.rb +1 -1
- data/lib/devise/models/confirmable.rb +2 -2
- data/lib/devise/models/database_authenticatable.rb +12 -2
- data/lib/devise/models/recoverable.rb +2 -6
- data/lib/devise/rails/routes.rb +17 -3
- data/lib/devise/strategies/authenticatable.rb +1 -1
- data/lib/devise/version.rb +1 -1
- data/lib/generators/devise/views_generator.rb +14 -3
- data/lib/generators/templates/devise.rb +3 -0
- data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
- data/lib/generators/templates/markerb/password_change.markerb +3 -0
- data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
- data/lib/generators/templates/markerb/unlock_instructions.markerb +1 -1
- data/test/controllers/helper_methods_test.rb +21 -0
- data/test/failure_app_test.rb +17 -0
- data/test/generators/views_generator_test.rb +7 -0
- data/test/integration/omniauthable_test.rb +11 -9
- data/test/integration/timeoutable_test.rb +12 -0
- data/test/models/confirmable_test.rb +10 -0
- data/test/models/database_authenticatable_test.rb +20 -0
- data/test/models/lockable_test.rb +1 -1
- data/test/models/recoverable_test.rb +23 -0
- data/test/models_test.rb +15 -6
- data/test/rails_app/app/active_record/user_without_email.rb +8 -0
- data/test/rails_app/app/mongoid/user_without_email.rb +33 -0
- data/test/rails_app/config/routes.rb +5 -0
- data/test/rails_app/lib/shared_user_without_email.rb +26 -0
- data/test/support/helpers.rb +4 -0
- metadata +33 -22
@@ -16,10 +16,6 @@ module Devise
|
|
16
16
|
# # resets the user password and save the record, true if valid passwords are given, otherwise false
|
17
17
|
# User.find(1).reset_password('password123', 'password123')
|
18
18
|
#
|
19
|
-
# # only resets the user password, without saving the record
|
20
|
-
# user = User.find(1)
|
21
|
-
# user.reset_password('password123', 'password123')
|
22
|
-
#
|
23
19
|
# # creates a new token and send it with instructions about how to reset the password
|
24
20
|
# User.find(1).send_reset_password_instructions
|
25
21
|
#
|
@@ -31,8 +27,8 @@ module Devise
|
|
31
27
|
end
|
32
28
|
|
33
29
|
included do
|
34
|
-
|
35
|
-
if email_changed? || encrypted_password_changed?
|
30
|
+
before_update do
|
31
|
+
if (respond_to?(:email_changed?) && email_changed?) || encrypted_password_changed?
|
36
32
|
clear_reset_password_token
|
37
33
|
end
|
38
34
|
end
|
data/lib/devise/rails/routes.rb
CHANGED
@@ -94,10 +94,24 @@ module ActionDispatch::Routing
|
|
94
94
|
#
|
95
95
|
# devise_for :users, path: 'accounts'
|
96
96
|
#
|
97
|
-
# * singular: setup the singular name for the given resource. This is used as the
|
98
|
-
#
|
97
|
+
# * singular: setup the singular name for the given resource. This is used as the helper methods
|
98
|
+
# names in controller ("authenticate_#{singular}!", "#{singular}_signed_in?", "current_#{singular}"
|
99
|
+
# and "#{singular}_session"), as the scope name in routes and as the scope given to warden.
|
99
100
|
#
|
100
|
-
# devise_for :
|
101
|
+
# devise_for :admins, singular: :manager
|
102
|
+
#
|
103
|
+
# devise_scope :manager do
|
104
|
+
# ...
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# class ManagerController < ApplicationController
|
108
|
+
# before_filter authenticate_manager!
|
109
|
+
#
|
110
|
+
# def show
|
111
|
+
# @manager = current_manager
|
112
|
+
# ...
|
113
|
+
# end
|
114
|
+
# end
|
101
115
|
#
|
102
116
|
# * path_names: configure different path names to overwrite defaults :sign_in, :sign_out, :sign_up,
|
103
117
|
# :password, :confirmation, :unlock.
|
@@ -27,7 +27,7 @@ module Devise
|
|
27
27
|
|
28
28
|
# Receives a resource and check if it is valid by calling valid_for_authentication?
|
29
29
|
# An optional block that will be triggered while validating can be optionally
|
30
|
-
# given as parameter. Check Devise::Models::
|
30
|
+
# given as parameter. Check Devise::Models::Authenticatable.valid_for_authentication?
|
31
31
|
# for more information.
|
32
32
|
#
|
33
33
|
# In case the resource can't be validated, it will fail with the given
|
data/lib/devise/version.rb
CHANGED
@@ -47,7 +47,7 @@ module Devise
|
|
47
47
|
def view_directory(name, _target_path = nil)
|
48
48
|
directory name.to_s, _target_path || "#{target_path}/#{name}" do |content|
|
49
49
|
if scope
|
50
|
-
content.gsub "devise/shared/links", "#{
|
50
|
+
content.gsub "devise/shared/links", "#{plural_scope}/shared/links"
|
51
51
|
else
|
52
52
|
content
|
53
53
|
end
|
@@ -55,7 +55,11 @@ module Devise
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def target_path
|
58
|
-
@target_path ||= "app/views/#{
|
58
|
+
@target_path ||= "app/views/#{plural_scope || :devise}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def plural_scope
|
62
|
+
@plural_scope ||= scope.presence && scope.underscore.pluralize
|
59
63
|
end
|
60
64
|
end
|
61
65
|
|
@@ -83,6 +87,13 @@ module Devise
|
|
83
87
|
source_root File.expand_path("../../templates/simple_form_for", __FILE__)
|
84
88
|
desc "Copies simple form enabled views to your application."
|
85
89
|
hide!
|
90
|
+
|
91
|
+
def copy_views
|
92
|
+
if options[:views]
|
93
|
+
options[:views].delete('mailer')
|
94
|
+
end
|
95
|
+
super
|
96
|
+
end
|
86
97
|
end
|
87
98
|
|
88
99
|
class ErbGenerator < Rails::Generators::Base #:nodoc:
|
@@ -111,7 +122,7 @@ module Devise
|
|
111
122
|
end
|
112
123
|
|
113
124
|
def target_path
|
114
|
-
"app/views/#{
|
125
|
+
"app/views/#{plural_scope || :devise}/mailer"
|
115
126
|
end
|
116
127
|
end
|
117
128
|
|
@@ -105,6 +105,9 @@ Devise.setup do |config|
|
|
105
105
|
# Setup a pepper to generate the encrypted password.
|
106
106
|
# config.pepper = '<%= SecureRandom.hex(64) %>'
|
107
107
|
|
108
|
+
# Send a notification email when the user's password is changed
|
109
|
+
# config.send_password_change_notification = false
|
110
|
+
|
108
111
|
# ==> Configuration for :confirmable
|
109
112
|
# A period that the user is allowed to access the website even without
|
110
113
|
# confirming their account. For instance, if set to 2.days, the user will be
|
@@ -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
|
-
|
5
|
+
[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
|
-
|
7
|
+
[Unlock my account](<%= unlock_url(@resource, unlock_token: @token) %>)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ApiController < ActionController::Metal
|
4
|
+
include Devise::Controllers::Helpers
|
5
|
+
end
|
6
|
+
|
7
|
+
class HelperMethodsTest < ActionController::TestCase
|
8
|
+
tests ApiController
|
9
|
+
|
10
|
+
test 'includes Devise::Controllers::Helpers' do
|
11
|
+
assert_includes @controller.class.ancestors, Devise::Controllers::Helpers
|
12
|
+
end
|
13
|
+
|
14
|
+
test 'does not respond_to helper_method' do
|
15
|
+
refute_respond_to @controller.class, :helper_method
|
16
|
+
end
|
17
|
+
|
18
|
+
test 'defines methods like current_user' do
|
19
|
+
assert_respond_to @controller, :current_user
|
20
|
+
end
|
21
|
+
end
|
data/test/failure_app_test.rb
CHANGED
@@ -294,5 +294,22 @@ class FailureTest < ActiveSupport::TestCase
|
|
294
294
|
assert @response.third.body.include?('<h2>Log in</h2>')
|
295
295
|
assert @response.third.body.include?('Your account is not activated yet.')
|
296
296
|
end
|
297
|
+
|
298
|
+
if Rails.application.config.respond_to?(:relative_url_root)
|
299
|
+
test 'calls the original controller with the proper environment considering the relative url root' do
|
300
|
+
swap Rails.application.config, relative_url_root: "/sample" do
|
301
|
+
env = {
|
302
|
+
"warden.options" => { recall: "devise/sessions#new", attempted_path: "/sample/users/sign_in"},
|
303
|
+
"devise.mapping" => Devise.mappings[:user],
|
304
|
+
"warden" => stub_everything
|
305
|
+
}
|
306
|
+
call_failure(env)
|
307
|
+
assert @response.third.body.include?('<h2>Log in</h2>')
|
308
|
+
assert @response.third.body.include?('Invalid email or password.')
|
309
|
+
assert_equal @request.env["SCRIPT_NAME"], '/sample'
|
310
|
+
assert_equal @request.env["PATH_INFO"], '/users/sign_in'
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
297
314
|
end
|
298
315
|
end
|
@@ -46,6 +46,13 @@ class ViewsGeneratorTest < Rails::Generators::TestCase
|
|
46
46
|
assert_no_file "app/views/devise/mailer/confirmation_instructions.html.erb"
|
47
47
|
end
|
48
48
|
|
49
|
+
test "Assert mailer specific directory with simple form" do
|
50
|
+
run_generator %w(-v mailer -b simple_form_for)
|
51
|
+
assert_file "app/views/devise/mailer/confirmation_instructions.html.erb"
|
52
|
+
assert_file "app/views/devise/mailer/reset_password_instructions.html.erb"
|
53
|
+
assert_file "app/views/devise/mailer/unlock_instructions.html.erb"
|
54
|
+
end
|
55
|
+
|
49
56
|
test "Assert specified directories with scope" do
|
50
57
|
run_generator %w(users -v sessions)
|
51
58
|
assert_file "app/views/users/sessions/new.html.erb"
|
@@ -20,9 +20,11 @@ class OmniauthableIntegrationTest < ActionDispatch::IntegrationTest
|
|
20
20
|
"credentials" => {"token" => 'plataformatec'},
|
21
21
|
"extra" => {"user_hash" => FACEBOOK_INFO}
|
22
22
|
}
|
23
|
+
OmniAuth.config.add_camelization 'facebook', 'FaceBook'
|
23
24
|
end
|
24
25
|
|
25
26
|
teardown do
|
27
|
+
OmniAuth.config.camelizations.delete('facebook')
|
26
28
|
OmniAuth.config.test_mode = false
|
27
29
|
end
|
28
30
|
|
@@ -40,7 +42,7 @@ class OmniauthableIntegrationTest < ActionDispatch::IntegrationTest
|
|
40
42
|
|
41
43
|
test "can access omniauth.auth in the env hash" do
|
42
44
|
visit "/users/sign_in"
|
43
|
-
click_link "Sign in with
|
45
|
+
click_link "Sign in with FaceBook"
|
44
46
|
|
45
47
|
json = ActiveSupport::JSON.decode(response.body)
|
46
48
|
|
@@ -54,7 +56,7 @@ class OmniauthableIntegrationTest < ActionDispatch::IntegrationTest
|
|
54
56
|
test "cleans up session on sign up" do
|
55
57
|
assert_no_difference "User.count" do
|
56
58
|
visit "/users/sign_in"
|
57
|
-
click_link "Sign in with
|
59
|
+
click_link "Sign in with FaceBook"
|
58
60
|
end
|
59
61
|
|
60
62
|
assert session["devise.facebook_data"]
|
@@ -75,7 +77,7 @@ class OmniauthableIntegrationTest < ActionDispatch::IntegrationTest
|
|
75
77
|
test "cleans up session on cancel" do
|
76
78
|
assert_no_difference "User.count" do
|
77
79
|
visit "/users/sign_in"
|
78
|
-
click_link "Sign in with
|
80
|
+
click_link "Sign in with FaceBook"
|
79
81
|
end
|
80
82
|
|
81
83
|
assert session["devise.facebook_data"]
|
@@ -86,7 +88,7 @@ class OmniauthableIntegrationTest < ActionDispatch::IntegrationTest
|
|
86
88
|
test "cleans up session on sign in" do
|
87
89
|
assert_no_difference "User.count" do
|
88
90
|
visit "/users/sign_in"
|
89
|
-
click_link "Sign in with
|
91
|
+
click_link "Sign in with FaceBook"
|
90
92
|
end
|
91
93
|
|
92
94
|
assert session["devise.facebook_data"]
|
@@ -96,13 +98,13 @@ class OmniauthableIntegrationTest < ActionDispatch::IntegrationTest
|
|
96
98
|
|
97
99
|
test "sign in and send remember token if configured" do
|
98
100
|
visit "/users/sign_in"
|
99
|
-
click_link "Sign in with
|
101
|
+
click_link "Sign in with FaceBook"
|
100
102
|
assert_nil warden.cookies["remember_user_token"]
|
101
103
|
|
102
104
|
stub_action!(:sign_in_facebook) do
|
103
105
|
create_user
|
104
106
|
visit "/users/sign_in"
|
105
|
-
click_link "Sign in with
|
107
|
+
click_link "Sign in with FaceBook"
|
106
108
|
assert warden.authenticated?(:user)
|
107
109
|
assert warden.cookies["remember_user_token"]
|
108
110
|
end
|
@@ -118,16 +120,16 @@ class OmniauthableIntegrationTest < ActionDispatch::IntegrationTest
|
|
118
120
|
OmniAuth.config.mock_auth[:facebook] = :access_denied
|
119
121
|
visit "/users/auth/facebook/callback?error=access_denied"
|
120
122
|
assert_current_url "/users/sign_in"
|
121
|
-
assert_contain 'Could not authenticate you from
|
123
|
+
assert_contain 'Could not authenticate you from FaceBook because "Access denied".'
|
122
124
|
end
|
123
125
|
|
124
126
|
test "handles other exceptions from OmniAuth" do
|
125
127
|
OmniAuth.config.mock_auth[:facebook] = :invalid_credentials
|
126
128
|
|
127
129
|
visit "/users/sign_in"
|
128
|
-
click_link "Sign in with
|
130
|
+
click_link "Sign in with FaceBook"
|
129
131
|
|
130
132
|
assert_current_url "/users/sign_in"
|
131
|
-
assert_contain 'Could not authenticate you from
|
133
|
+
assert_contain 'Could not authenticate you from FaceBook because "Invalid credentials".'
|
132
134
|
end
|
133
135
|
end
|
@@ -24,6 +24,18 @@ class SessionTimeoutTest < ActionDispatch::IntegrationTest
|
|
24
24
|
assert_equal old_last_request, last_request_at
|
25
25
|
end
|
26
26
|
|
27
|
+
test 'does not set last request at in user session after each request if timeoutable is disabled' do
|
28
|
+
sign_in_as_user
|
29
|
+
old_last_request = last_request_at
|
30
|
+
assert_not_nil last_request_at
|
31
|
+
|
32
|
+
new_time = 2.seconds.from_now
|
33
|
+
Time.stubs(:now).returns(new_time)
|
34
|
+
|
35
|
+
get users_path, {}, 'devise.skip_timeoutable' => true
|
36
|
+
assert_equal old_last_request, last_request_at
|
37
|
+
end
|
38
|
+
|
27
39
|
test 'does not time out user session before default limit time' do
|
28
40
|
sign_in_as_user
|
29
41
|
assert_response :success
|
@@ -250,6 +250,16 @@ class ConfirmableTest < ActiveSupport::TestCase
|
|
250
250
|
assert user.reload.active_for_authentication?
|
251
251
|
end
|
252
252
|
|
253
|
+
test 'should not break when a user tries to reset their password in the case where confirmation is not required and confirm_within is set' do
|
254
|
+
swap Devise, confirm_within: 3.days do
|
255
|
+
user = create_user
|
256
|
+
user.instance_eval { def confirmation_required?; false end }
|
257
|
+
user.confirmation_sent_at = nil
|
258
|
+
user.save
|
259
|
+
assert user.reload.confirm!
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
253
263
|
test 'should find a user to send email instructions for the user confirm its email by authentication_keys' do
|
254
264
|
swap Devise, authentication_keys: [:username, :email] do
|
255
265
|
user = create_user
|
@@ -3,6 +3,10 @@ require 'test_models'
|
|
3
3
|
require 'digest/sha1'
|
4
4
|
|
5
5
|
class DatabaseAuthenticatableTest < ActiveSupport::TestCase
|
6
|
+
def setup
|
7
|
+
setup_mailer
|
8
|
+
end
|
9
|
+
|
6
10
|
test 'should downcase case insensitive keys when saving' do
|
7
11
|
# case_insensitive_keys is set to :email by default.
|
8
12
|
email = 'Foo@Bar.com'
|
@@ -225,6 +229,22 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
|
|
225
229
|
assert_match "can't be blank", user.errors[:current_password].join
|
226
230
|
end
|
227
231
|
|
232
|
+
test 'should not email on password change' do
|
233
|
+
user = create_user
|
234
|
+
assert_email_not_sent do
|
235
|
+
assert user.update_attributes(password: 'newpass', password_confirmation: 'newpass')
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
test 'should email on password change when configured' do
|
240
|
+
swap Devise, send_password_change_notification: true do
|
241
|
+
user = create_user
|
242
|
+
assert_email_sent user.email do
|
243
|
+
assert user.update_attributes(password: 'newpass', password_confirmation: 'newpass')
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
228
248
|
test 'downcase_keys with validation' do
|
229
249
|
User.create(email: "HEllO@example.com", password: "123456")
|
230
250
|
user = User.create(email: "HEllO@example.com", password: "123456")
|
@@ -14,7 +14,7 @@ class LockableTest < ActiveSupport::TestCase
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
test "should increment failed_attempts on
|
17
|
+
test "should increment failed_attempts on successful validation if the user is already locked" do
|
18
18
|
user = create_user
|
19
19
|
user.confirm
|
20
20
|
|
@@ -42,6 +42,17 @@ class RecoverableTest < ActiveSupport::TestCase
|
|
42
42
|
assert_nil user.reset_password_token
|
43
43
|
end
|
44
44
|
|
45
|
+
test 'should not clear reset password token for new user' do
|
46
|
+
user = new_user
|
47
|
+
assert_nil user.reset_password_token
|
48
|
+
|
49
|
+
user.send_reset_password_instructions
|
50
|
+
assert_present user.reset_password_token
|
51
|
+
|
52
|
+
user.save
|
53
|
+
assert_present user.reset_password_token
|
54
|
+
end
|
55
|
+
|
45
56
|
test 'should clear reset password token if changing password' do
|
46
57
|
user = create_user
|
47
58
|
assert_nil user.reset_password_token
|
@@ -65,6 +76,18 @@ class RecoverableTest < ActiveSupport::TestCase
|
|
65
76
|
assert_nil user.reset_password_token
|
66
77
|
end
|
67
78
|
|
79
|
+
test 'should clear reset password successfully even if there is no email' do
|
80
|
+
user = create_user_without_email
|
81
|
+
assert_nil user.reset_password_token
|
82
|
+
|
83
|
+
user.send_reset_password_instructions
|
84
|
+
assert_present user.reset_password_token
|
85
|
+
user.password = "123456678"
|
86
|
+
user.password_confirmation = "123456678"
|
87
|
+
user.save!
|
88
|
+
assert_nil user.reset_password_token
|
89
|
+
end
|
90
|
+
|
68
91
|
test 'should not clear reset password token if record is invalid' do
|
69
92
|
user = create_user
|
70
93
|
user.send_reset_password_instructions
|
data/test/models_test.rb
CHANGED
@@ -92,13 +92,20 @@ class ActiveRecordTest < ActiveSupport::TestCase
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
+
module StubModelFilters
|
96
|
+
def stub_filter(name)
|
97
|
+
define_singleton_method(name) { |*| nil }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
95
101
|
class CheckFieldsTest < ActiveSupport::TestCase
|
96
102
|
test 'checks if the class respond_to the required fields' do
|
97
103
|
Player = Class.new do
|
98
104
|
extend Devise::Models
|
105
|
+
extend StubModelFilters
|
99
106
|
|
100
|
-
|
101
|
-
|
107
|
+
stub_filter :before_validation
|
108
|
+
stub_filter :after_update
|
102
109
|
|
103
110
|
devise :database_authenticatable
|
104
111
|
|
@@ -113,9 +120,10 @@ class CheckFieldsTest < ActiveSupport::TestCase
|
|
113
120
|
test 'raises Devise::Models::MissingAtrribute and shows the missing attribute if the class doesn\'t respond_to one of the attributes' do
|
114
121
|
Clown = Class.new do
|
115
122
|
extend Devise::Models
|
123
|
+
extend StubModelFilters
|
116
124
|
|
117
|
-
|
118
|
-
|
125
|
+
stub_filter :before_validation
|
126
|
+
stub_filter :after_update
|
119
127
|
|
120
128
|
devise :database_authenticatable
|
121
129
|
|
@@ -130,9 +138,10 @@ class CheckFieldsTest < ActiveSupport::TestCase
|
|
130
138
|
test 'raises Devise::Models::MissingAtrribute with all the missing attributes if there is more than one' do
|
131
139
|
Magician = Class.new do
|
132
140
|
extend Devise::Models
|
141
|
+
extend StubModelFilters
|
133
142
|
|
134
|
-
|
135
|
-
|
143
|
+
stub_filter :before_validation
|
144
|
+
stub_filter :after_update
|
136
145
|
|
137
146
|
devise :database_authenticatable
|
138
147
|
end
|