devise 3.2.2 → 4.8.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +281 -957
- data/MIT-LICENSE +2 -1
- data/README.md +410 -120
- data/app/controllers/devise/confirmations_controller.rb +11 -5
- data/app/controllers/devise/omniauth_callbacks_controller.rb +12 -6
- data/app/controllers/devise/passwords_controller.rb +21 -8
- data/app/controllers/devise/registrations_controller.rb +59 -26
- data/app/controllers/devise/sessions_controller.rb +47 -17
- data/app/controllers/devise/unlocks_controller.rb +9 -4
- data/app/controllers/devise_controller.rb +69 -33
- data/app/helpers/devise_helper.rb +23 -18
- data/app/mailers/devise/mailer.rb +13 -3
- data/app/views/devise/confirmations/new.html.erb +9 -5
- data/app/views/devise/mailer/confirmation_instructions.html.erb +1 -1
- data/app/views/devise/mailer/email_changed.html.erb +7 -0
- data/app/views/devise/mailer/password_change.html.erb +3 -0
- data/app/views/devise/mailer/reset_password_instructions.html.erb +1 -1
- data/app/views/devise/mailer/unlock_instructions.html.erb +1 -1
- data/app/views/devise/passwords/edit.html.erb +16 -7
- data/app/views/devise/passwords/new.html.erb +9 -5
- data/app/views/devise/registrations/edit.html.erb +29 -15
- data/app/views/devise/registrations/new.html.erb +20 -9
- data/app/views/devise/sessions/new.html.erb +19 -10
- data/app/views/devise/shared/_error_messages.html.erb +15 -0
- data/app/views/devise/shared/{_links.erb → _links.html.erb} +9 -9
- data/app/views/devise/unlocks/new.html.erb +9 -5
- data/config/locales/en.yml +24 -18
- data/lib/devise/controllers/helpers.rb +113 -33
- data/lib/devise/controllers/rememberable.rb +15 -6
- data/lib/devise/controllers/scoped_views.rb +3 -1
- data/lib/devise/controllers/sign_in_out.rb +47 -29
- data/lib/devise/controllers/store_location.rb +31 -5
- data/lib/devise/controllers/url_helpers.rb +10 -8
- data/lib/devise/delegator.rb +2 -0
- data/lib/devise/encryptor.rb +24 -0
- data/lib/devise/failure_app.rb +119 -40
- data/lib/devise/hooks/activatable.rb +7 -6
- data/lib/devise/hooks/csrf_cleaner.rb +5 -1
- data/lib/devise/hooks/forgetable.rb +2 -0
- data/lib/devise/hooks/lockable.rb +5 -3
- data/lib/devise/hooks/proxy.rb +4 -2
- data/lib/devise/hooks/rememberable.rb +4 -2
- data/lib/devise/hooks/timeoutable.rb +16 -9
- data/lib/devise/hooks/trackable.rb +3 -1
- data/lib/devise/mailers/helpers.rb +15 -12
- data/lib/devise/mapping.rb +9 -3
- data/lib/devise/models/authenticatable.rb +91 -61
- data/lib/devise/models/confirmable.rb +138 -43
- data/lib/devise/models/database_authenticatable.rb +112 -31
- data/lib/devise/models/lockable.rb +39 -18
- data/lib/devise/models/omniauthable.rb +3 -1
- data/lib/devise/models/recoverable.rb +64 -28
- data/lib/devise/models/registerable.rb +4 -0
- data/lib/devise/models/rememberable.rb +62 -33
- data/lib/devise/models/timeoutable.rb +4 -8
- data/lib/devise/models/trackable.rb +20 -4
- data/lib/devise/models/validatable.rb +16 -9
- data/lib/devise/models.rb +3 -1
- data/lib/devise/modules.rb +12 -10
- data/lib/devise/omniauth/config.rb +2 -0
- data/lib/devise/omniauth/url_helpers.rb +14 -5
- data/lib/devise/omniauth.rb +4 -5
- data/lib/devise/orm/active_record.rb +5 -1
- data/lib/devise/orm/mongoid.rb +6 -2
- data/lib/devise/parameter_filter.rb +4 -0
- data/lib/devise/parameter_sanitizer.rb +139 -65
- data/lib/devise/rails/deprecated_constant_accessor.rb +39 -0
- data/lib/devise/rails/routes.rb +151 -120
- data/lib/devise/rails/warden_compat.rb +3 -10
- data/lib/devise/rails.rb +10 -13
- data/lib/devise/secret_key_finder.rb +27 -0
- data/lib/devise/strategies/authenticatable.rb +21 -10
- data/lib/devise/strategies/base.rb +3 -1
- data/lib/devise/strategies/database_authenticatable.rb +14 -6
- data/lib/devise/strategies/rememberable.rb +15 -3
- data/lib/devise/test/controller_helpers.rb +167 -0
- data/lib/devise/test/integration_helpers.rb +63 -0
- data/lib/devise/test_helpers.rb +7 -124
- data/lib/devise/time_inflector.rb +4 -2
- data/lib/devise/token_generator.rb +3 -41
- data/lib/devise/version.rb +3 -1
- data/lib/devise.rb +107 -84
- data/lib/generators/active_record/devise_generator.rb +64 -12
- data/lib/generators/active_record/templates/migration.rb +9 -7
- data/lib/generators/active_record/templates/migration_existing.rb +9 -7
- data/lib/generators/devise/controllers_generator.rb +46 -0
- data/lib/generators/devise/devise_generator.rb +8 -6
- data/lib/generators/devise/install_generator.rb +18 -1
- data/lib/generators/devise/orm_helpers.rb +10 -21
- data/lib/generators/devise/views_generator.rb +49 -28
- data/lib/generators/mongoid/devise_generator.rb +21 -19
- data/lib/generators/templates/README +13 -12
- data/lib/generators/templates/controllers/README +14 -0
- data/lib/generators/templates/controllers/confirmations_controller.rb +30 -0
- data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +30 -0
- data/lib/generators/templates/controllers/passwords_controller.rb +34 -0
- data/lib/generators/templates/controllers/registrations_controller.rb +62 -0
- data/lib/generators/templates/controllers/sessions_controller.rb +27 -0
- data/lib/generators/templates/controllers/unlocks_controller.rb +30 -0
- data/lib/generators/templates/devise.rb +94 -37
- data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
- data/lib/generators/templates/markerb/email_changed.markerb +7 -0
- 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/lib/generators/templates/simple_form_for/confirmations/new.html.erb +6 -2
- data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +12 -4
- data/lib/generators/templates/simple_form_for/passwords/new.html.erb +5 -2
- data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +14 -6
- data/lib/generators/templates/simple_form_for/registrations/new.html.erb +12 -4
- data/lib/generators/templates/simple_form_for/sessions/new.html.erb +11 -6
- data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +5 -2
- metadata +50 -284
- data/.gitignore +0 -10
- data/.travis.yml +0 -20
- data/.yardopts +0 -9
- data/CONTRIBUTING.md +0 -14
- data/Gemfile +0 -31
- data/Gemfile.lock +0 -160
- data/Rakefile +0 -35
- data/devise.gemspec +0 -27
- data/devise.png +0 -0
- data/gemfiles/Gemfile.rails-3.2.x +0 -31
- data/gemfiles/Gemfile.rails-3.2.x.lock +0 -159
- data/test/controllers/custom_strategy_test.rb +0 -62
- data/test/controllers/helpers_test.rb +0 -276
- data/test/controllers/internal_helpers_test.rb +0 -120
- data/test/controllers/passwords_controller_test.rb +0 -31
- data/test/controllers/sessions_controller_test.rb +0 -99
- data/test/controllers/url_helpers_test.rb +0 -59
- data/test/delegator_test.rb +0 -19
- data/test/devise_test.rb +0 -94
- data/test/failure_app_test.rb +0 -232
- data/test/generators/active_record_generator_test.rb +0 -103
- data/test/generators/devise_generator_test.rb +0 -39
- data/test/generators/install_generator_test.rb +0 -13
- data/test/generators/mongoid_generator_test.rb +0 -23
- data/test/generators/views_generator_test.rb +0 -67
- data/test/helpers/devise_helper_test.rb +0 -51
- data/test/integration/authenticatable_test.rb +0 -713
- data/test/integration/confirmable_test.rb +0 -284
- data/test/integration/database_authenticatable_test.rb +0 -84
- data/test/integration/http_authenticatable_test.rb +0 -105
- data/test/integration/lockable_test.rb +0 -239
- data/test/integration/omniauthable_test.rb +0 -133
- data/test/integration/recoverable_test.rb +0 -334
- data/test/integration/registerable_test.rb +0 -349
- data/test/integration/rememberable_test.rb +0 -167
- data/test/integration/timeoutable_test.rb +0 -183
- data/test/integration/trackable_test.rb +0 -92
- data/test/mailers/confirmation_instructions_test.rb +0 -115
- data/test/mailers/reset_password_instructions_test.rb +0 -96
- data/test/mailers/unlock_instructions_test.rb +0 -91
- data/test/mapping_test.rb +0 -127
- data/test/models/authenticatable_test.rb +0 -13
- data/test/models/confirmable_test.rb +0 -454
- data/test/models/database_authenticatable_test.rb +0 -249
- data/test/models/lockable_test.rb +0 -298
- data/test/models/omniauthable_test.rb +0 -7
- data/test/models/recoverable_test.rb +0 -184
- data/test/models/registerable_test.rb +0 -7
- data/test/models/rememberable_test.rb +0 -183
- data/test/models/serializable_test.rb +0 -49
- data/test/models/timeoutable_test.rb +0 -51
- data/test/models/trackable_test.rb +0 -13
- data/test/models/validatable_test.rb +0 -127
- data/test/models_test.rb +0 -144
- data/test/omniauth/config_test.rb +0 -57
- data/test/omniauth/url_helpers_test.rb +0 -54
- data/test/orm/active_record.rb +0 -10
- data/test/orm/mongoid.rb +0 -13
- data/test/parameter_sanitizer_test.rb +0 -81
- data/test/rails_app/Rakefile +0 -6
- data/test/rails_app/app/active_record/admin.rb +0 -6
- data/test/rails_app/app/active_record/shim.rb +0 -2
- data/test/rails_app/app/active_record/user.rb +0 -6
- data/test/rails_app/app/controllers/admins/sessions_controller.rb +0 -6
- data/test/rails_app/app/controllers/admins_controller.rb +0 -11
- data/test/rails_app/app/controllers/application_controller.rb +0 -9
- data/test/rails_app/app/controllers/home_controller.rb +0 -25
- data/test/rails_app/app/controllers/publisher/registrations_controller.rb +0 -2
- data/test/rails_app/app/controllers/publisher/sessions_controller.rb +0 -2
- data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +0 -14
- data/test/rails_app/app/controllers/users_controller.rb +0 -31
- data/test/rails_app/app/helpers/application_helper.rb +0 -3
- data/test/rails_app/app/mailers/users/mailer.rb +0 -12
- data/test/rails_app/app/mongoid/admin.rb +0 -29
- data/test/rails_app/app/mongoid/shim.rb +0 -23
- data/test/rails_app/app/mongoid/user.rb +0 -39
- data/test/rails_app/app/views/admins/index.html.erb +0 -1
- data/test/rails_app/app/views/admins/sessions/new.html.erb +0 -2
- data/test/rails_app/app/views/home/admin_dashboard.html.erb +0 -1
- data/test/rails_app/app/views/home/index.html.erb +0 -1
- data/test/rails_app/app/views/home/join.html.erb +0 -1
- data/test/rails_app/app/views/home/private.html.erb +0 -1
- data/test/rails_app/app/views/home/user_dashboard.html.erb +0 -1
- data/test/rails_app/app/views/layouts/application.html.erb +0 -24
- data/test/rails_app/app/views/users/edit_form.html.erb +0 -1
- data/test/rails_app/app/views/users/index.html.erb +0 -1
- data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +0 -1
- data/test/rails_app/app/views/users/sessions/new.html.erb +0 -1
- data/test/rails_app/bin/bundle +0 -3
- data/test/rails_app/bin/rails +0 -4
- data/test/rails_app/bin/rake +0 -4
- data/test/rails_app/config/application.rb +0 -40
- data/test/rails_app/config/boot.rb +0 -14
- data/test/rails_app/config/database.yml +0 -18
- data/test/rails_app/config/environment.rb +0 -5
- data/test/rails_app/config/environments/development.rb +0 -30
- data/test/rails_app/config/environments/production.rb +0 -80
- data/test/rails_app/config/environments/test.rb +0 -36
- data/test/rails_app/config/initializers/backtrace_silencers.rb +0 -7
- data/test/rails_app/config/initializers/devise.rb +0 -181
- data/test/rails_app/config/initializers/inflections.rb +0 -2
- data/test/rails_app/config/initializers/secret_token.rb +0 -8
- data/test/rails_app/config/initializers/session_store.rb +0 -1
- data/test/rails_app/config/routes.rb +0 -104
- data/test/rails_app/config.ru +0 -4
- data/test/rails_app/db/migrate/20100401102949_create_tables.rb +0 -71
- data/test/rails_app/db/schema.rb +0 -55
- data/test/rails_app/lib/shared_admin.rb +0 -17
- data/test/rails_app/lib/shared_user.rb +0 -29
- data/test/rails_app/public/404.html +0 -26
- data/test/rails_app/public/422.html +0 -26
- data/test/rails_app/public/500.html +0 -26
- data/test/rails_app/public/favicon.ico +0 -0
- data/test/routes_test.rb +0 -250
- data/test/support/assertions.rb +0 -40
- data/test/support/helpers.rb +0 -70
- data/test/support/integration.rb +0 -92
- data/test/support/locale/en.yml +0 -8
- data/test/support/webrat/integrations/rails.rb +0 -24
- data/test/test_helper.rb +0 -27
- data/test/test_helpers_test.rb +0 -173
- data/test/test_models.rb +0 -33
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'devise/hooks/activatable'
|
|
2
4
|
require 'devise/hooks/csrf_cleaner'
|
|
5
|
+
require 'devise/rails/deprecated_constant_accessor'
|
|
3
6
|
|
|
4
7
|
module Devise
|
|
5
8
|
module Models
|
|
@@ -29,7 +32,7 @@ module Devise
|
|
|
29
32
|
# It also accepts an array specifying the strategies that should allow params authentication.
|
|
30
33
|
#
|
|
31
34
|
# * +skip_session_storage+: By default Devise will store the user in session.
|
|
32
|
-
# By default is set to :
|
|
35
|
+
# By default is set to skip_session_storage: [:http_auth].
|
|
33
36
|
#
|
|
34
37
|
# == active_for_authentication?
|
|
35
38
|
#
|
|
@@ -37,7 +40,7 @@ module Devise
|
|
|
37
40
|
# calling model.active_for_authentication?. This method is overwritten by other devise modules. For instance,
|
|
38
41
|
# :confirmable overwrites .active_for_authentication? to only return true if your model was confirmed.
|
|
39
42
|
#
|
|
40
|
-
# You overwrite this method yourself, but if you do, don't forget to call super:
|
|
43
|
+
# You can overwrite this method yourself, but if you do, don't forget to call super:
|
|
41
44
|
#
|
|
42
45
|
# def active_for_authentication?
|
|
43
46
|
# super && special_condition_is_valid?
|
|
@@ -53,13 +56,16 @@ module Devise
|
|
|
53
56
|
module Authenticatable
|
|
54
57
|
extend ActiveSupport::Concern
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
UNSAFE_ATTRIBUTES_FOR_SERIALIZATION = [:encrypted_password, :reset_password_token, :reset_password_sent_at,
|
|
57
60
|
:remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip,
|
|
58
61
|
:last_sign_in_ip, :password_salt, :confirmation_token, :confirmed_at, :confirmation_sent_at,
|
|
59
|
-
:remember_token, :unconfirmed_email, :failed_attempts, :unlock_token, :locked_at
|
|
62
|
+
:remember_token, :unconfirmed_email, :failed_attempts, :unlock_token, :locked_at]
|
|
63
|
+
|
|
64
|
+
include Devise::DeprecatedConstantAccessor
|
|
65
|
+
deprecate_constant "BLACKLIST_FOR_SERIALIZATION", "Devise::Models::Authenticatable::UNSAFE_ATTRIBUTES_FOR_SERIALIZATION"
|
|
60
66
|
|
|
61
67
|
included do
|
|
62
|
-
class_attribute :devise_modules, :
|
|
68
|
+
class_attribute :devise_modules, instance_writer: false
|
|
63
69
|
self.devise_modules ||= []
|
|
64
70
|
|
|
65
71
|
before_validation :downcase_keys
|
|
@@ -95,29 +101,31 @@ module Devise
|
|
|
95
101
|
def authenticatable_salt
|
|
96
102
|
end
|
|
97
103
|
|
|
98
|
-
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
104
|
+
# Redefine serializable_hash in models for more secure defaults.
|
|
105
|
+
# By default, it removes from the serializable model all attributes that
|
|
106
|
+
# are *not* accessible. You can remove this default by using :force_except
|
|
107
|
+
# and passing a new list of attributes you want to exempt. All attributes
|
|
108
|
+
# given to :except will simply add names to exempt to Devise internal list.
|
|
109
|
+
def serializable_hash(options = nil)
|
|
110
|
+
options = options.try(:dup) || {}
|
|
111
|
+
options[:except] = Array(options[:except]).dup
|
|
112
|
+
|
|
113
|
+
if options[:force_except]
|
|
114
|
+
options[:except].concat Array(options[:force_except])
|
|
115
|
+
else
|
|
116
|
+
options[:except].concat UNSAFE_ATTRIBUTES_FOR_SERIALIZATION
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
super(options)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Redefine inspect using serializable_hash, to ensure we don't accidentally
|
|
123
|
+
# leak passwords into exceptions.
|
|
124
|
+
def inspect
|
|
125
|
+
inspection = serializable_hash.collect do |k,v|
|
|
126
|
+
"#{k}: #{respond_to?(:attribute_for_inspect) ? attribute_for_inspect(k) : v.inspect}"
|
|
127
|
+
end
|
|
128
|
+
"#<#{self.class} #{inspection.join(", ")}>"
|
|
121
129
|
end
|
|
122
130
|
|
|
123
131
|
protected
|
|
@@ -127,18 +135,20 @@ module Devise
|
|
|
127
135
|
end
|
|
128
136
|
|
|
129
137
|
# This is an internal method called every time Devise needs
|
|
130
|
-
# to send a notification/mail. This can be
|
|
138
|
+
# to send a notification/mail. This can be overridden if you
|
|
131
139
|
# need to customize the e-mail delivery logic. For instance,
|
|
132
|
-
# if you are using a queue to deliver e-mails (
|
|
133
|
-
# sidekiq, resque, etc), you must add the delivery to the queue
|
|
140
|
+
# if you are using a queue to deliver e-mails (active job, delayed
|
|
141
|
+
# job, sidekiq, resque, etc), you must add the delivery to the queue
|
|
134
142
|
# just after the transaction was committed. To achieve this,
|
|
135
143
|
# you can override send_devise_notification to store the
|
|
136
|
-
# deliveries until the after_commit callback is triggered
|
|
144
|
+
# deliveries until the after_commit callback is triggered.
|
|
145
|
+
#
|
|
146
|
+
# The following example uses Active Job's `deliver_later` :
|
|
137
147
|
#
|
|
138
148
|
# class User
|
|
139
149
|
# devise :database_authenticatable, :confirmable
|
|
140
150
|
#
|
|
141
|
-
# after_commit :
|
|
151
|
+
# after_commit :send_pending_devise_notifications
|
|
142
152
|
#
|
|
143
153
|
# protected
|
|
144
154
|
#
|
|
@@ -146,31 +156,55 @@ module Devise
|
|
|
146
156
|
# # If the record is new or changed then delay the
|
|
147
157
|
# # delivery until the after_commit callback otherwise
|
|
148
158
|
# # send now because after_commit will not be called.
|
|
149
|
-
#
|
|
150
|
-
#
|
|
159
|
+
# # For Rails < 6 use `changed?` instead of `saved_changes?`.
|
|
160
|
+
# if new_record? || saved_changes?
|
|
161
|
+
# pending_devise_notifications << [notification, args]
|
|
151
162
|
# else
|
|
152
|
-
#
|
|
163
|
+
# render_and_send_devise_message(notification, *args)
|
|
153
164
|
# end
|
|
154
165
|
# end
|
|
155
166
|
#
|
|
156
|
-
#
|
|
157
|
-
#
|
|
158
|
-
#
|
|
167
|
+
# private
|
|
168
|
+
#
|
|
169
|
+
# def send_pending_devise_notifications
|
|
170
|
+
# pending_devise_notifications.each do |notification, args|
|
|
171
|
+
# render_and_send_devise_message(notification, *args)
|
|
159
172
|
# end
|
|
160
173
|
#
|
|
161
174
|
# # Empty the pending notifications array because the
|
|
162
175
|
# # after_commit hook can be called multiple times which
|
|
163
176
|
# # could cause multiple emails to be sent.
|
|
164
|
-
#
|
|
177
|
+
# pending_devise_notifications.clear
|
|
178
|
+
# end
|
|
179
|
+
#
|
|
180
|
+
# def pending_devise_notifications
|
|
181
|
+
# @pending_devise_notifications ||= []
|
|
165
182
|
# end
|
|
166
183
|
#
|
|
167
|
-
# def
|
|
168
|
-
#
|
|
184
|
+
# def render_and_send_devise_message(notification, *args)
|
|
185
|
+
# message = devise_mailer.send(notification, self, *args)
|
|
186
|
+
#
|
|
187
|
+
# # Deliver later with Active Job's `deliver_later`
|
|
188
|
+
# if message.respond_to?(:deliver_later)
|
|
189
|
+
# message.deliver_later
|
|
190
|
+
# # Remove once we move to Rails 4.2+ only, as `deliver` is deprecated.
|
|
191
|
+
# elsif message.respond_to?(:deliver_now)
|
|
192
|
+
# message.deliver_now
|
|
193
|
+
# else
|
|
194
|
+
# message.deliver
|
|
195
|
+
# end
|
|
169
196
|
# end
|
|
197
|
+
#
|
|
170
198
|
# end
|
|
171
199
|
#
|
|
172
200
|
def send_devise_notification(notification, *args)
|
|
173
|
-
devise_mailer.send(notification, self, *args)
|
|
201
|
+
message = devise_mailer.send(notification, self, *args)
|
|
202
|
+
# Remove once we move to Rails 4.2+ only.
|
|
203
|
+
if message.respond_to?(:deliver_now)
|
|
204
|
+
message.deliver_now
|
|
205
|
+
else
|
|
206
|
+
message.deliver
|
|
207
|
+
end
|
|
174
208
|
end
|
|
175
209
|
|
|
176
210
|
def downcase_keys
|
|
@@ -231,46 +265,42 @@ module Devise
|
|
|
231
265
|
# Example:
|
|
232
266
|
#
|
|
233
267
|
# def self.find_for_authentication(tainted_conditions)
|
|
234
|
-
# find_first_by_auth_conditions(tainted_conditions, :
|
|
268
|
+
# find_first_by_auth_conditions(tainted_conditions, active: true)
|
|
235
269
|
# end
|
|
236
270
|
#
|
|
237
271
|
# Finally, notice that Devise also queries for users in other scenarios
|
|
238
|
-
# besides authentication, for example when retrieving
|
|
272
|
+
# besides authentication, for example when retrieving a user to send
|
|
239
273
|
# an e-mail for password reset. In such cases, find_for_authentication
|
|
240
274
|
# is not called.
|
|
241
275
|
def find_for_authentication(tainted_conditions)
|
|
242
276
|
find_first_by_auth_conditions(tainted_conditions)
|
|
243
277
|
end
|
|
244
278
|
|
|
245
|
-
def find_first_by_auth_conditions(tainted_conditions, opts={})
|
|
279
|
+
def find_first_by_auth_conditions(tainted_conditions, opts = {})
|
|
246
280
|
to_adapter.find_first(devise_parameter_filter.filter(tainted_conditions).merge(opts))
|
|
247
281
|
end
|
|
248
282
|
|
|
249
|
-
# Find
|
|
250
|
-
def find_or_initialize_with_error_by(attribute, value, error
|
|
283
|
+
# Find or initialize a record setting an error if it can't be found.
|
|
284
|
+
def find_or_initialize_with_error_by(attribute, value, error = :invalid) #:nodoc:
|
|
251
285
|
find_or_initialize_with_errors([attribute], { attribute => value }, error)
|
|
252
286
|
end
|
|
253
287
|
|
|
254
|
-
# Find
|
|
255
|
-
def find_or_initialize_with_errors(required_attributes, attributes, error
|
|
256
|
-
attributes
|
|
257
|
-
attributes
|
|
288
|
+
# Find or initialize a record with group of attributes based on a list of required attributes.
|
|
289
|
+
def find_or_initialize_with_errors(required_attributes, attributes, error = :invalid) #:nodoc:
|
|
290
|
+
attributes.try(:permit!)
|
|
291
|
+
attributes = attributes.to_h.with_indifferent_access
|
|
292
|
+
.slice(*required_attributes)
|
|
293
|
+
.delete_if { |key, value| value.blank? }
|
|
258
294
|
|
|
259
295
|
if attributes.size == required_attributes.size
|
|
260
|
-
record = find_first_by_auth_conditions(attributes)
|
|
296
|
+
record = find_first_by_auth_conditions(attributes) and return record
|
|
261
297
|
end
|
|
262
298
|
|
|
263
|
-
|
|
264
|
-
record = new
|
|
265
|
-
|
|
299
|
+
new(devise_parameter_filter.filter(attributes)).tap do |record|
|
|
266
300
|
required_attributes.each do |key|
|
|
267
|
-
|
|
268
|
-
record.send("#{key}=", value)
|
|
269
|
-
record.errors.add(key, value.present? ? error : :blank)
|
|
301
|
+
record.errors.add(key, attributes[key].blank? ? :blank : error)
|
|
270
302
|
end
|
|
271
303
|
end
|
|
272
|
-
|
|
273
|
-
record
|
|
274
304
|
end
|
|
275
305
|
|
|
276
306
|
protected
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Devise
|
|
2
4
|
module Models
|
|
3
5
|
# Confirmable is responsible to verify if an account is already confirmed to
|
|
@@ -5,42 +7,60 @@ module Devise
|
|
|
5
7
|
# Confirmation instructions are sent to the user email after creating a
|
|
6
8
|
# record and when manually requested by a new confirmation instruction request.
|
|
7
9
|
#
|
|
10
|
+
# Confirmable tracks the following columns:
|
|
11
|
+
#
|
|
12
|
+
# * confirmation_token - A unique random token
|
|
13
|
+
# * confirmed_at - A timestamp when the user clicked the confirmation link
|
|
14
|
+
# * confirmation_sent_at - A timestamp when the confirmation_token was generated (not sent)
|
|
15
|
+
# * unconfirmed_email - An email address copied from the email attr. After confirmation
|
|
16
|
+
# this value is copied to the email attr then cleared
|
|
17
|
+
#
|
|
8
18
|
# == Options
|
|
9
19
|
#
|
|
10
20
|
# Confirmable adds the following options to +devise+:
|
|
11
21
|
#
|
|
12
|
-
# * +allow_unconfirmed_access_for+: the time you want to allow the user to access
|
|
22
|
+
# * +allow_unconfirmed_access_for+: the time you want to allow the user to access their account
|
|
13
23
|
# before confirming it. After this period, the user access is denied. You can
|
|
14
24
|
# use this to let your user access some features of your application without
|
|
15
25
|
# confirming the account, but blocking it after a certain period (ie 7 days).
|
|
16
26
|
# By default allow_unconfirmed_access_for is zero, it means users always have to confirm to sign in.
|
|
17
27
|
# * +reconfirmable+: requires any email changes to be confirmed (exactly the same way as
|
|
18
28
|
# initial account confirmation) to be applied. Requires additional unconfirmed_email
|
|
19
|
-
# db field to be
|
|
29
|
+
# db field to be set up (t.reconfirmable in migrations). Until confirmed, new email is
|
|
20
30
|
# stored in unconfirmed email column, and copied to email column on successful
|
|
21
|
-
# confirmation.
|
|
31
|
+
# confirmation. Also, when used in conjunction with `send_email_changed_notification`,
|
|
32
|
+
# the notification is sent to the original email when the change is requested,
|
|
33
|
+
# not when the unconfirmed email is confirmed.
|
|
22
34
|
# * +confirm_within+: the time before a sent confirmation token becomes invalid.
|
|
23
35
|
# You can use this to force the user to confirm within a set period of time.
|
|
36
|
+
# Confirmable will not generate a new token if a repeat confirmation is requested
|
|
37
|
+
# during this time frame, unless the user's email changed too.
|
|
24
38
|
#
|
|
25
39
|
# == Examples
|
|
26
40
|
#
|
|
27
|
-
# User.find(1).confirm
|
|
41
|
+
# User.find(1).confirm # returns true unless it's already confirmed
|
|
28
42
|
# User.find(1).confirmed? # true/false
|
|
29
43
|
# User.find(1).send_confirmation_instructions # manually send instructions
|
|
30
44
|
#
|
|
31
45
|
module Confirmable
|
|
32
46
|
extend ActiveSupport::Concern
|
|
33
|
-
include ActionView::Helpers::DateHelper
|
|
34
47
|
|
|
35
48
|
included do
|
|
36
|
-
before_create :generate_confirmation_token, :
|
|
37
|
-
after_create
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
before_create :generate_confirmation_token, if: :confirmation_required?
|
|
50
|
+
after_create :skip_reconfirmation_in_callback!, if: :send_confirmation_notification?
|
|
51
|
+
if defined?(ActiveRecord) && self < ActiveRecord::Base # ActiveRecord
|
|
52
|
+
after_commit :send_on_create_confirmation_instructions, on: :create, if: :send_confirmation_notification?
|
|
53
|
+
after_commit :send_reconfirmation_instructions, on: :update, if: :reconfirmation_required?
|
|
54
|
+
else # Mongoid
|
|
55
|
+
after_create :send_on_create_confirmation_instructions, if: :send_confirmation_notification?
|
|
56
|
+
after_update :send_reconfirmation_instructions, if: :reconfirmation_required?
|
|
57
|
+
end
|
|
58
|
+
before_update :postpone_email_change_until_confirmation_and_regenerate_confirmation_token, if: :postpone_email_change?
|
|
40
59
|
end
|
|
41
60
|
|
|
42
61
|
def initialize(*args, &block)
|
|
43
62
|
@bypass_confirmation_postpone = false
|
|
63
|
+
@skip_reconfirmation_in_callback = false
|
|
44
64
|
@reconfirmation_required = false
|
|
45
65
|
@skip_confirmation_notification = false
|
|
46
66
|
@raw_confirmation_token = nil
|
|
@@ -56,26 +76,25 @@ module Devise
|
|
|
56
76
|
# Confirm a user by setting it's confirmed_at to actual time. If the user
|
|
57
77
|
# is already confirmed, add an error to email field. If the user is invalid
|
|
58
78
|
# add errors
|
|
59
|
-
def confirm
|
|
79
|
+
def confirm(args = {})
|
|
60
80
|
pending_any_confirmation do
|
|
61
81
|
if confirmation_period_expired?
|
|
62
82
|
self.errors.add(:email, :confirmation_period_expired,
|
|
63
|
-
:
|
|
83
|
+
period: Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago))
|
|
64
84
|
return false
|
|
65
85
|
end
|
|
66
86
|
|
|
67
|
-
self.confirmation_token = nil
|
|
68
87
|
self.confirmed_at = Time.now.utc
|
|
69
88
|
|
|
70
|
-
saved = if
|
|
89
|
+
saved = if pending_reconfirmation?
|
|
71
90
|
skip_reconfirmation!
|
|
72
91
|
self.email = unconfirmed_email
|
|
73
92
|
self.unconfirmed_email = nil
|
|
74
93
|
|
|
75
94
|
# We need to validate in such cases to enforce e-mail uniqueness
|
|
76
|
-
save(:
|
|
95
|
+
save(validate: true)
|
|
77
96
|
else
|
|
78
|
-
save(:
|
|
97
|
+
save(validate: args[:ensure_valid] == true)
|
|
79
98
|
end
|
|
80
99
|
|
|
81
100
|
after_confirmation if saved
|
|
@@ -98,7 +117,7 @@ module Devise
|
|
|
98
117
|
generate_confirmation_token!
|
|
99
118
|
end
|
|
100
119
|
|
|
101
|
-
opts = pending_reconfirmation? ? { :
|
|
120
|
+
opts = pending_reconfirmation? ? { to: unconfirmed_email } : { }
|
|
102
121
|
send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
|
|
103
122
|
end
|
|
104
123
|
|
|
@@ -151,8 +170,14 @@ module Devise
|
|
|
151
170
|
|
|
152
171
|
protected
|
|
153
172
|
|
|
173
|
+
# To not require reconfirmation after creating with #save called in a
|
|
174
|
+
# callback call skip_create_confirmation!
|
|
175
|
+
def skip_reconfirmation_in_callback!
|
|
176
|
+
@skip_reconfirmation_in_callback = true
|
|
177
|
+
end
|
|
178
|
+
|
|
154
179
|
# A callback method used to deliver confirmation
|
|
155
|
-
# instructions on creation. This can be
|
|
180
|
+
# instructions on creation. This can be overridden
|
|
156
181
|
# in models to map to a nice sign up e-mail.
|
|
157
182
|
def send_on_create_confirmation_instructions
|
|
158
183
|
send_confirmation_instructions
|
|
@@ -166,7 +191,7 @@ module Devise
|
|
|
166
191
|
# Checks if the confirmation for the user is within the limit time.
|
|
167
192
|
# We do this by calculating if the difference between today and the
|
|
168
193
|
# confirmation sent date does not exceed the confirm in time configured.
|
|
169
|
-
#
|
|
194
|
+
# allow_unconfirmed_access_for is a model configuration, must always be an integer value.
|
|
170
195
|
#
|
|
171
196
|
# Example:
|
|
172
197
|
#
|
|
@@ -186,7 +211,10 @@ module Devise
|
|
|
186
211
|
# confirmation_period_valid? # will always return true
|
|
187
212
|
#
|
|
188
213
|
def confirmation_period_valid?
|
|
189
|
-
|
|
214
|
+
return true if self.class.allow_unconfirmed_access_for.nil?
|
|
215
|
+
return false if self.class.allow_unconfirmed_access_for == 0.days
|
|
216
|
+
|
|
217
|
+
confirmation_sent_at && confirmation_sent_at.utc >= self.class.allow_unconfirmed_access_for.ago
|
|
190
218
|
end
|
|
191
219
|
|
|
192
220
|
# Checks if the user confirmation happens before the token becomes invalid
|
|
@@ -202,7 +230,7 @@ module Devise
|
|
|
202
230
|
# confirmation_period_expired? # will always return false
|
|
203
231
|
#
|
|
204
232
|
def confirmation_period_expired?
|
|
205
|
-
self.class.confirm_within && (Time.now > self.confirmation_sent_at + self.class.confirm_within
|
|
233
|
+
self.class.confirm_within && self.confirmation_sent_at && (Time.now.utc > self.confirmation_sent_at.utc + self.class.confirm_within)
|
|
206
234
|
end
|
|
207
235
|
|
|
208
236
|
# Checks whether the record requires any confirmation.
|
|
@@ -216,39 +244,88 @@ module Devise
|
|
|
216
244
|
end
|
|
217
245
|
|
|
218
246
|
# Generates a new random token for confirmation, and stores
|
|
219
|
-
# the time this token is being generated
|
|
247
|
+
# the time this token is being generated in confirmation_sent_at
|
|
220
248
|
def generate_confirmation_token
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
249
|
+
if self.confirmation_token && !confirmation_period_expired?
|
|
250
|
+
@raw_confirmation_token = self.confirmation_token
|
|
251
|
+
else
|
|
252
|
+
self.confirmation_token = @raw_confirmation_token = Devise.friendly_token
|
|
253
|
+
self.confirmation_sent_at = Time.now.utc
|
|
254
|
+
end
|
|
225
255
|
end
|
|
226
256
|
|
|
227
257
|
def generate_confirmation_token!
|
|
228
|
-
generate_confirmation_token && save(:
|
|
258
|
+
generate_confirmation_token && save(validate: false)
|
|
229
259
|
end
|
|
230
260
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
261
|
+
if Devise.activerecord51?
|
|
262
|
+
def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
|
|
263
|
+
@reconfirmation_required = true
|
|
264
|
+
self.unconfirmed_email = self.email
|
|
265
|
+
self.email = self.email_in_database
|
|
266
|
+
self.confirmation_token = nil
|
|
267
|
+
generate_confirmation_token
|
|
268
|
+
end
|
|
269
|
+
else
|
|
270
|
+
def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
|
|
271
|
+
@reconfirmation_required = true
|
|
272
|
+
self.unconfirmed_email = self.email
|
|
273
|
+
self.email = self.email_was
|
|
274
|
+
self.confirmation_token = nil
|
|
275
|
+
generate_confirmation_token
|
|
276
|
+
end
|
|
236
277
|
end
|
|
237
278
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
279
|
+
if Devise.activerecord51?
|
|
280
|
+
def postpone_email_change?
|
|
281
|
+
postpone = self.class.reconfirmable &&
|
|
282
|
+
will_save_change_to_email? &&
|
|
283
|
+
!@bypass_confirmation_postpone &&
|
|
284
|
+
self.email.present? &&
|
|
285
|
+
(!@skip_reconfirmation_in_callback || !self.email_in_database.nil?)
|
|
286
|
+
@bypass_confirmation_postpone = false
|
|
287
|
+
postpone
|
|
288
|
+
end
|
|
289
|
+
else
|
|
290
|
+
def postpone_email_change?
|
|
291
|
+
postpone = self.class.reconfirmable &&
|
|
292
|
+
email_changed? &&
|
|
293
|
+
!@bypass_confirmation_postpone &&
|
|
294
|
+
self.email.present? &&
|
|
295
|
+
(!@skip_reconfirmation_in_callback || !self.email_was.nil?)
|
|
296
|
+
@bypass_confirmation_postpone = false
|
|
297
|
+
postpone
|
|
298
|
+
end
|
|
242
299
|
end
|
|
243
300
|
|
|
244
301
|
def reconfirmation_required?
|
|
245
|
-
self.class.reconfirmable && @reconfirmation_required &&
|
|
302
|
+
self.class.reconfirmable && @reconfirmation_required && (self.email.present? || self.unconfirmed_email.present?)
|
|
246
303
|
end
|
|
247
304
|
|
|
248
305
|
def send_confirmation_notification?
|
|
249
|
-
confirmation_required? && !@skip_confirmation_notification &&
|
|
306
|
+
confirmation_required? && !@skip_confirmation_notification && self.email.present?
|
|
250
307
|
end
|
|
251
308
|
|
|
309
|
+
# With reconfirmable, notify the original email when the user first
|
|
310
|
+
# requests the email change, instead of when the change is confirmed.
|
|
311
|
+
def send_email_changed_notification?
|
|
312
|
+
if self.class.reconfirmable
|
|
313
|
+
self.class.send_email_changed_notification && reconfirmation_required?
|
|
314
|
+
else
|
|
315
|
+
super
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# A callback initiated after successfully confirming. This can be
|
|
320
|
+
# used to insert your own logic that is only run after the user successfully
|
|
321
|
+
# confirms.
|
|
322
|
+
#
|
|
323
|
+
# Example:
|
|
324
|
+
#
|
|
325
|
+
# def after_confirmation
|
|
326
|
+
# self.update_attribute(:invite_code, nil)
|
|
327
|
+
# end
|
|
328
|
+
#
|
|
252
329
|
def after_confirmation
|
|
253
330
|
end
|
|
254
331
|
|
|
@@ -257,7 +334,7 @@ module Devise
|
|
|
257
334
|
# confirmation instructions to it. If not, try searching for a user by unconfirmed_email
|
|
258
335
|
# field. If no user is found, returns a new user with an email not found error.
|
|
259
336
|
# Options must contain the user email
|
|
260
|
-
def send_confirmation_instructions(attributes={})
|
|
337
|
+
def send_confirmation_instructions(attributes = {})
|
|
261
338
|
confirmable = find_by_unconfirmed_email_with_errors(attributes) if reconfirmable
|
|
262
339
|
unless confirmable.try(:persisted?)
|
|
263
340
|
confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
|
|
@@ -271,17 +348,35 @@ module Devise
|
|
|
271
348
|
# If the user is already confirmed, create an error for the user
|
|
272
349
|
# Options must have the confirmation_token
|
|
273
350
|
def confirm_by_token(confirmation_token)
|
|
274
|
-
|
|
275
|
-
confirmation_token
|
|
351
|
+
# When the `confirmation_token` parameter is blank, if there are any users with a blank
|
|
352
|
+
# `confirmation_token` in the database, the first one would be confirmed here.
|
|
353
|
+
# The error is being manually added here to ensure no users are confirmed by mistake.
|
|
354
|
+
# This was done in the model for convenience, since validation errors are automatically
|
|
355
|
+
# displayed in the view.
|
|
356
|
+
if confirmation_token.blank?
|
|
357
|
+
confirmable = new
|
|
358
|
+
confirmable.errors.add(:confirmation_token, :blank)
|
|
359
|
+
return confirmable
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
confirmable = find_first_by_auth_conditions(confirmation_token: confirmation_token)
|
|
363
|
+
|
|
364
|
+
unless confirmable
|
|
365
|
+
confirmation_digest = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
|
|
366
|
+
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_digest)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# TODO: replace above lines with
|
|
370
|
+
# confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
|
|
371
|
+
# after enough time has passed that Devise clients do not use digested tokens
|
|
276
372
|
|
|
277
|
-
confirmable
|
|
278
|
-
confirmable.confirm! if confirmable.persisted?
|
|
279
|
-
confirmable.confirmation_token = original_token
|
|
373
|
+
confirmable.confirm if confirmable.persisted?
|
|
280
374
|
confirmable
|
|
281
375
|
end
|
|
282
376
|
|
|
283
377
|
# Find a record for confirmation by unconfirmed email field
|
|
284
378
|
def find_by_unconfirmed_email_with_errors(attributes = {})
|
|
379
|
+
attributes = attributes.slice(*confirmation_keys).permit!.to_h if attributes.respond_to? :permit
|
|
285
380
|
unconfirmed_required_attributes = confirmation_keys.map { |k| k == :email ? :unconfirmed_email : k }
|
|
286
381
|
unconfirmed_attributes = attributes.symbolize_keys
|
|
287
382
|
unconfirmed_attributes[:unconfirmed_email] = unconfirmed_attributes.delete(:email)
|