devise 3.0.0 → 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 +351 -0
- data/MIT-LICENSE +2 -1
- data/README.md +422 -130
- data/app/controllers/devise/confirmations_controller.rb +17 -6
- data/app/controllers/devise/omniauth_callbacks_controller.rb +12 -6
- data/app/controllers/devise/passwords_controller.rb +23 -8
- data/app/controllers/devise/registrations_controller.rb +70 -28
- data/app/controllers/devise/sessions_controller.rb +49 -17
- data/app/controllers/devise/unlocks_controller.rb +11 -4
- data/app/controllers/devise_controller.rb +74 -34
- data/app/helpers/devise_helper.rb +23 -18
- data/app/mailers/devise/mailer.rb +25 -10
- 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} +10 -10
- data/app/views/devise/unlocks/new.html.erb +9 -5
- data/config/locales/en.yml +26 -20
- data/lib/devise/controllers/helpers.rb +122 -125
- data/lib/devise/controllers/rememberable.rb +14 -14
- data/lib/devise/controllers/scoped_views.rb +3 -1
- data/lib/devise/controllers/sign_in_out.rb +121 -0
- data/lib/devise/controllers/store_location.rb +76 -0
- 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 +132 -42
- data/lib/devise/hooks/activatable.rb +7 -6
- data/lib/devise/hooks/csrf_cleaner.rb +9 -0
- data/lib/devise/hooks/forgetable.rb +3 -1
- data/lib/devise/hooks/lockable.rb +5 -3
- data/lib/devise/hooks/proxy.rb +23 -0
- data/lib/devise/hooks/rememberable.rb +7 -4
- data/lib/devise/hooks/timeoutable.rb +18 -8
- data/lib/devise/hooks/trackable.rb +3 -1
- data/lib/devise/mailers/helpers.rb +15 -18
- data/lib/devise/mapping.rb +9 -3
- data/lib/devise/models/authenticatable.rb +102 -80
- data/lib/devise/models/confirmable.rb +154 -72
- data/lib/devise/models/database_authenticatable.rb +125 -25
- data/lib/devise/models/lockable.rb +50 -29
- data/lib/devise/models/omniauthable.rb +3 -1
- data/lib/devise/models/recoverable.rb +72 -50
- data/lib/devise/models/registerable.rb +4 -0
- data/lib/devise/models/rememberable.rb +65 -32
- 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 +6 -13
- data/lib/devise/modules.rb +12 -11
- 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 +144 -34
- data/lib/devise/rails/deprecated_constant_accessor.rb +39 -0
- data/lib/devise/rails/routes.rb +191 -127
- data/lib/devise/rails/warden_compat.rb +2 -1
- data/lib/devise/rails.rb +13 -20
- data/lib/devise/secret_key_finder.rb +27 -0
- data/lib/devise/strategies/authenticatable.rb +21 -22
- data/lib/devise/strategies/base.rb +3 -1
- data/lib/devise/strategies/database_authenticatable.rb +15 -4
- 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 -123
- data/lib/devise/time_inflector.rb +4 -2
- data/lib/devise/token_generator.rb +32 -0
- data/lib/devise/version.rb +3 -1
- data/lib/devise.rb +124 -78
- data/lib/generators/active_record/devise_generator.rb +64 -15
- data/lib/generators/active_record/templates/migration.rb +9 -8
- data/lib/generators/active_record/templates/migration_existing.rb +9 -8
- data/lib/generators/devise/controllers_generator.rb +46 -0
- data/lib/generators/devise/devise_generator.rb +10 -6
- data/lib/generators/devise/install_generator.rb +19 -1
- data/lib/generators/devise/orm_helpers.rb +17 -9
- data/lib/generators/devise/views_generator.rb +51 -28
- data/lib/generators/mongoid/devise_generator.rb +24 -24
- 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 +118 -53
- 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 +73 -294
- data/.gitignore +0 -10
- data/.travis.yml +0 -20
- data/.yardopts +0 -9
- data/CHANGELOG.rdoc +0 -941
- data/CONTRIBUTING.md +0 -14
- data/Gemfile +0 -31
- data/Gemfile.lock +0 -159
- data/Rakefile +0 -35
- data/app/views/devise/_links.erb +0 -3
- data/devise.gemspec +0 -26
- data/devise.png +0 -0
- data/gemfiles/Gemfile.rails-3.2.x +0 -31
- data/gemfiles/Gemfile.rails-3.2.x.lock +0 -156
- data/lib/devise/models/token_authenticatable.rb +0 -89
- data/lib/devise/strategies/token_authenticatable.rb +0 -91
- data/test/controllers/custom_strategy_test.rb +0 -62
- data/test/controllers/helpers_test.rb +0 -253
- data/test/controllers/internal_helpers_test.rb +0 -120
- data/test/controllers/passwords_controller_test.rb +0 -32
- 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 -83
- data/test/failure_app_test.rb +0 -221
- data/test/generators/active_record_generator_test.rb +0 -73
- 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 -699
- data/test/integration/confirmable_test.rb +0 -299
- data/test/integration/database_authenticatable_test.rb +0 -84
- data/test/integration/http_authenticatable_test.rb +0 -115
- data/test/integration/lockable_test.rb +0 -242
- data/test/integration/omniauthable_test.rb +0 -133
- data/test/integration/recoverable_test.rb +0 -335
- data/test/integration/registerable_test.rb +0 -349
- data/test/integration/rememberable_test.rb +0 -165
- data/test/integration/timeoutable_test.rb +0 -150
- data/test/integration/token_authenticatable_test.rb +0 -205
- data/test/integration/trackable_test.rb +0 -92
- data/test/mailers/confirmation_instructions_test.rb +0 -111
- data/test/mailers/reset_password_instructions_test.rb +0 -92
- data/test/mailers/unlock_instructions_test.rb +0 -87
- data/test/mapping_test.rb +0 -127
- data/test/models/authenticatable_test.rb +0 -13
- data/test/models/confirmable_test.rb +0 -452
- data/test/models/database_authenticatable_test.rb +0 -226
- data/test/models/lockable_test.rb +0 -282
- data/test/models/omniauthable_test.rb +0 -7
- data/test/models/recoverable_test.rb +0 -222
- data/test/models/registerable_test.rb +0 -7
- data/test/models/rememberable_test.rb +0 -175
- data/test/models/serializable_test.rb +0 -49
- data/test/models/timeoutable_test.rb +0 -46
- data/test/models/token_authenticatable_test.rb +0 -55
- data/test/models/trackable_test.rb +0 -13
- data/test/models/validatable_test.rb +0 -127
- data/test/models_test.rb +0 -163
- 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 -58
- 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 -42
- 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 -8
- 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 -34
- data/test/rails_app/config/environments/production.rb +0 -84
- 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 -178
- 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 -74
- data/test/rails_app/db/schema.rb +0 -52
- data/test/rails_app/lib/shared_admin.rb +0 -14
- data/test/rails_app/lib/shared_user.rb +0 -25
- 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 -91
- data/test/support/integration.rb +0 -92
- data/test/support/locale/en.yml +0 -4
- data/test/support/webrat/integrations/rails.rb +0 -24
- data/test/test_helper.rb +0 -34
- data/test/test_helpers_test.rb +0 -151
- data/test/test_models.rb +0 -26
|
@@ -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,44 +7,63 @@ 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
|
-
# Confirmable adds the following options to
|
|
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
|
|
66
|
+
@raw_confirmation_token = nil
|
|
46
67
|
super
|
|
47
68
|
end
|
|
48
69
|
|
|
@@ -55,27 +76,29 @@ module Devise
|
|
|
55
76
|
# Confirm a user by setting it's confirmed_at to actual time. If the user
|
|
56
77
|
# is already confirmed, add an error to email field. If the user is invalid
|
|
57
78
|
# add errors
|
|
58
|
-
def confirm
|
|
79
|
+
def confirm(args = {})
|
|
59
80
|
pending_any_confirmation do
|
|
60
81
|
if confirmation_period_expired?
|
|
61
82
|
self.errors.add(:email, :confirmation_period_expired,
|
|
62
|
-
:
|
|
83
|
+
period: Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago))
|
|
63
84
|
return false
|
|
64
85
|
end
|
|
65
86
|
|
|
66
|
-
self.confirmation_token = nil
|
|
67
87
|
self.confirmed_at = Time.now.utc
|
|
68
88
|
|
|
69
|
-
|
|
89
|
+
saved = if pending_reconfirmation?
|
|
70
90
|
skip_reconfirmation!
|
|
71
91
|
self.email = unconfirmed_email
|
|
72
92
|
self.unconfirmed_email = nil
|
|
73
93
|
|
|
74
94
|
# We need to validate in such cases to enforce e-mail uniqueness
|
|
75
|
-
save(:
|
|
95
|
+
save(validate: true)
|
|
76
96
|
else
|
|
77
|
-
save(:
|
|
97
|
+
save(validate: args[:ensure_valid] == true)
|
|
78
98
|
end
|
|
99
|
+
|
|
100
|
+
after_confirmation if saved
|
|
101
|
+
saved
|
|
79
102
|
end
|
|
80
103
|
end
|
|
81
104
|
|
|
@@ -90,10 +113,12 @@ module Devise
|
|
|
90
113
|
|
|
91
114
|
# Send confirmation instructions by email
|
|
92
115
|
def send_confirmation_instructions
|
|
93
|
-
|
|
116
|
+
unless @raw_confirmation_token
|
|
117
|
+
generate_confirmation_token!
|
|
118
|
+
end
|
|
94
119
|
|
|
95
|
-
opts = pending_reconfirmation? ? { :
|
|
96
|
-
send_devise_notification(:confirmation_instructions, opts)
|
|
120
|
+
opts = pending_reconfirmation? ? { to: unconfirmed_email } : { }
|
|
121
|
+
send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
|
|
97
122
|
end
|
|
98
123
|
|
|
99
124
|
def send_reconfirmation_instructions
|
|
@@ -106,17 +131,11 @@ module Devise
|
|
|
106
131
|
|
|
107
132
|
# Resend confirmation token.
|
|
108
133
|
# Regenerates the token if the period is expired.
|
|
109
|
-
def
|
|
134
|
+
def resend_confirmation_instructions
|
|
110
135
|
pending_any_confirmation do
|
|
111
|
-
regenerate_confirmation_token! if confirmation_period_expired?
|
|
112
136
|
send_confirmation_instructions
|
|
113
137
|
end
|
|
114
138
|
end
|
|
115
|
-
|
|
116
|
-
# Generate a confirmation token unless already exists and save the record.
|
|
117
|
-
def ensure_confirmation_token!
|
|
118
|
-
generate_confirmation_token! if should_generate_confirmation_token?
|
|
119
|
-
end
|
|
120
139
|
|
|
121
140
|
# Overwrites active_for_authentication? for confirmation
|
|
122
141
|
# by verifying whether a user is active to sign in or not. If the user
|
|
@@ -146,19 +165,22 @@ module Devise
|
|
|
146
165
|
# If you don't want reconfirmation to be sent, neither a code
|
|
147
166
|
# to be generated, call skip_reconfirmation!
|
|
148
167
|
def skip_reconfirmation!
|
|
149
|
-
@
|
|
168
|
+
@bypass_confirmation_postpone = true
|
|
150
169
|
end
|
|
151
170
|
|
|
152
171
|
protected
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
155
177
|
end
|
|
156
178
|
|
|
157
179
|
# A callback method used to deliver confirmation
|
|
158
|
-
# instructions on creation. This can be
|
|
180
|
+
# instructions on creation. This can be overridden
|
|
159
181
|
# in models to map to a nice sign up e-mail.
|
|
160
182
|
def send_on_create_confirmation_instructions
|
|
161
|
-
|
|
183
|
+
send_confirmation_instructions
|
|
162
184
|
end
|
|
163
185
|
|
|
164
186
|
# Callback to overwrite if confirmation is required or not.
|
|
@@ -169,7 +191,7 @@ module Devise
|
|
|
169
191
|
# Checks if the confirmation for the user is within the limit time.
|
|
170
192
|
# We do this by calculating if the difference between today and the
|
|
171
193
|
# confirmation sent date does not exceed the confirm in time configured.
|
|
172
|
-
#
|
|
194
|
+
# allow_unconfirmed_access_for is a model configuration, must always be an integer value.
|
|
173
195
|
#
|
|
174
196
|
# Example:
|
|
175
197
|
#
|
|
@@ -189,7 +211,10 @@ module Devise
|
|
|
189
211
|
# confirmation_period_valid? # will always return true
|
|
190
212
|
#
|
|
191
213
|
def confirmation_period_valid?
|
|
192
|
-
|
|
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
|
|
193
218
|
end
|
|
194
219
|
|
|
195
220
|
# Checks if the user confirmation happens before the token becomes invalid
|
|
@@ -205,7 +230,7 @@ module Devise
|
|
|
205
230
|
# confirmation_period_expired? # will always return false
|
|
206
231
|
#
|
|
207
232
|
def confirmation_period_expired?
|
|
208
|
-
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)
|
|
209
234
|
end
|
|
210
235
|
|
|
211
236
|
# Checks whether the record requires any confirmation.
|
|
@@ -218,50 +243,90 @@ module Devise
|
|
|
218
243
|
end
|
|
219
244
|
end
|
|
220
245
|
|
|
221
|
-
# Generates a new random token for confirmation, and stores
|
|
222
|
-
# this token is being generated
|
|
246
|
+
# Generates a new random token for confirmation, and stores
|
|
247
|
+
# the time this token is being generated in confirmation_sent_at
|
|
223
248
|
def generate_confirmation_token
|
|
224
|
-
self.confirmation_token
|
|
225
|
-
|
|
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
|
|
226
255
|
end
|
|
227
256
|
|
|
228
257
|
def generate_confirmation_token!
|
|
229
|
-
generate_confirmation_token && save(:
|
|
258
|
+
generate_confirmation_token && save(validate: false)
|
|
230
259
|
end
|
|
231
260
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
|
239
277
|
end
|
|
240
278
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
|
244
299
|
end
|
|
245
300
|
|
|
246
|
-
def
|
|
247
|
-
@reconfirmation_required
|
|
248
|
-
self.unconfirmed_email = self.email
|
|
249
|
-
self.email = self.email_was
|
|
250
|
-
regenerate_confirmation_token
|
|
301
|
+
def reconfirmation_required?
|
|
302
|
+
self.class.reconfirmable && @reconfirmation_required && (self.email.present? || self.unconfirmed_email.present?)
|
|
251
303
|
end
|
|
252
304
|
|
|
253
|
-
def
|
|
254
|
-
|
|
255
|
-
@bypass_postpone = false
|
|
256
|
-
postpone
|
|
305
|
+
def send_confirmation_notification?
|
|
306
|
+
confirmation_required? && !@skip_confirmation_notification && self.email.present?
|
|
257
307
|
end
|
|
258
308
|
|
|
259
|
-
|
|
260
|
-
|
|
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
|
|
261
317
|
end
|
|
262
318
|
|
|
263
|
-
|
|
264
|
-
|
|
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
|
+
#
|
|
329
|
+
def after_confirmation
|
|
265
330
|
end
|
|
266
331
|
|
|
267
332
|
module ClassMethods
|
|
@@ -269,12 +334,12 @@ module Devise
|
|
|
269
334
|
# confirmation instructions to it. If not, try searching for a user by unconfirmed_email
|
|
270
335
|
# field. If no user is found, returns a new user with an email not found error.
|
|
271
336
|
# Options must contain the user email
|
|
272
|
-
def send_confirmation_instructions(attributes={})
|
|
337
|
+
def send_confirmation_instructions(attributes = {})
|
|
273
338
|
confirmable = find_by_unconfirmed_email_with_errors(attributes) if reconfirmable
|
|
274
339
|
unless confirmable.try(:persisted?)
|
|
275
340
|
confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
|
|
276
341
|
end
|
|
277
|
-
confirmable.
|
|
342
|
+
confirmable.resend_confirmation_instructions if confirmable.persisted?
|
|
278
343
|
confirmable
|
|
279
344
|
end
|
|
280
345
|
|
|
@@ -283,18 +348,35 @@ module Devise
|
|
|
283
348
|
# If the user is already confirmed, create an error for the user
|
|
284
349
|
# Options must have the confirmation_token
|
|
285
350
|
def confirm_by_token(confirmation_token)
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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)
|
|
290
363
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
|
372
|
+
|
|
373
|
+
confirmable.confirm if confirmable.persisted?
|
|
374
|
+
confirmable
|
|
294
375
|
end
|
|
295
376
|
|
|
296
377
|
# Find a record for confirmation by unconfirmed email field
|
|
297
378
|
def find_by_unconfirmed_email_with_errors(attributes = {})
|
|
379
|
+
attributes = attributes.slice(*confirmation_keys).permit!.to_h if attributes.respond_to? :permit
|
|
298
380
|
unconfirmed_required_attributes = confirmation_keys.map { |k| k == :email ? :unconfirmed_email : k }
|
|
299
381
|
unconfirmed_attributes = attributes.symbolize_keys
|
|
300
382
|
unconfirmed_attributes[:unconfirmed_email] = unconfirmed_attributes.delete(:email)
|
|
@@ -1,20 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'devise/strategies/database_authenticatable'
|
|
2
|
-
require 'bcrypt'
|
|
3
4
|
|
|
4
5
|
module Devise
|
|
5
6
|
module Models
|
|
6
|
-
# Authenticatable Module, responsible for
|
|
7
|
-
# authenticity of a user while signing in.
|
|
7
|
+
# Authenticatable Module, responsible for hashing the password and
|
|
8
|
+
# validating the authenticity of a user while signing in.
|
|
9
|
+
#
|
|
10
|
+
# This module defines a `password=` method. This method will hash the argument
|
|
11
|
+
# and store it in the `encrypted_password` column, bypassing any pre-existing
|
|
12
|
+
# `password` column if it exists.
|
|
8
13
|
#
|
|
9
14
|
# == Options
|
|
10
15
|
#
|
|
11
|
-
#
|
|
16
|
+
# DatabaseAuthenticatable adds the following options to devise_for:
|
|
12
17
|
#
|
|
13
18
|
# * +pepper+: a random string used to provide a more secure hash. Use
|
|
14
|
-
# `
|
|
19
|
+
# `rails secret` to generate new keys.
|
|
15
20
|
#
|
|
16
21
|
# * +stretches+: the cost given to bcrypt.
|
|
17
22
|
#
|
|
23
|
+
# * +send_email_changed_notification+: notify original email when it changes.
|
|
24
|
+
#
|
|
25
|
+
# * +send_password_change_notification+: notify email when password changes.
|
|
26
|
+
#
|
|
18
27
|
# == Examples
|
|
19
28
|
#
|
|
20
29
|
# User.find(1).valid_password?('password123') # returns true/false
|
|
@@ -23,26 +32,44 @@ module Devise
|
|
|
23
32
|
extend ActiveSupport::Concern
|
|
24
33
|
|
|
25
34
|
included do
|
|
35
|
+
after_update :send_email_changed_notification, if: :send_email_changed_notification?
|
|
36
|
+
after_update :send_password_change_notification, if: :send_password_change_notification?
|
|
37
|
+
|
|
26
38
|
attr_reader :password, :current_password
|
|
27
39
|
attr_accessor :password_confirmation
|
|
28
40
|
end
|
|
29
41
|
|
|
42
|
+
def initialize(*args, &block)
|
|
43
|
+
@skip_email_changed_notification = false
|
|
44
|
+
@skip_password_change_notification = false
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Skips sending the email changed notification after_update
|
|
49
|
+
def skip_email_changed_notification!
|
|
50
|
+
@skip_email_changed_notification = true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Skips sending the password change notification after_update
|
|
54
|
+
def skip_password_change_notification!
|
|
55
|
+
@skip_password_change_notification = true
|
|
56
|
+
end
|
|
57
|
+
|
|
30
58
|
def self.required_fields(klass)
|
|
31
59
|
[:encrypted_password] + klass.authentication_keys
|
|
32
60
|
end
|
|
33
61
|
|
|
34
|
-
# Generates password
|
|
62
|
+
# Generates a hashed password based on the given value.
|
|
63
|
+
# For legacy reasons, we use `encrypted_password` to store
|
|
64
|
+
# the hashed password.
|
|
35
65
|
def password=(new_password)
|
|
36
66
|
@password = new_password
|
|
37
67
|
self.encrypted_password = password_digest(@password) if @password.present?
|
|
38
68
|
end
|
|
39
69
|
|
|
40
|
-
# Verifies whether
|
|
70
|
+
# Verifies whether a password (ie from sign in) is the user password.
|
|
41
71
|
def valid_password?(password)
|
|
42
|
-
|
|
43
|
-
bcrypt = ::BCrypt::Password.new(encrypted_password)
|
|
44
|
-
password = ::BCrypt::Engine.hash_secret("#{password}#{self.class.pepper}", bcrypt.salt)
|
|
45
|
-
Devise.secure_compare(password, encrypted_password)
|
|
72
|
+
Devise::Encryptor.compare(self.class, encrypted_password, password)
|
|
46
73
|
end
|
|
47
74
|
|
|
48
75
|
# Set password and password confirmation to nil
|
|
@@ -50,10 +77,23 @@ module Devise
|
|
|
50
77
|
self.password = self.password_confirmation = nil
|
|
51
78
|
end
|
|
52
79
|
|
|
53
|
-
# Update record attributes when :current_password matches, otherwise
|
|
54
|
-
# error on :current_password.
|
|
55
|
-
#
|
|
80
|
+
# Update record attributes when :current_password matches, otherwise
|
|
81
|
+
# returns error on :current_password.
|
|
82
|
+
#
|
|
83
|
+
# This method also rejects the password field if it is blank (allowing
|
|
84
|
+
# users to change relevant information like the e-mail without changing
|
|
85
|
+
# their password). In case the password field is rejected, the confirmation
|
|
86
|
+
# is also rejected as long as it is also blank.
|
|
56
87
|
def update_with_password(params, *options)
|
|
88
|
+
if options.present?
|
|
89
|
+
ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
|
|
90
|
+
[Devise] The second argument of `DatabaseAuthenticatable#update_with_password`
|
|
91
|
+
(`options`) is deprecated and it will be removed in the next major version.
|
|
92
|
+
It was added to support a feature deprecated in Rails 4, so you can safely remove it
|
|
93
|
+
from your code.
|
|
94
|
+
DEPRECATION
|
|
95
|
+
end
|
|
96
|
+
|
|
57
97
|
current_password = params.delete(:current_password)
|
|
58
98
|
|
|
59
99
|
if params[:password].blank?
|
|
@@ -62,11 +102,11 @@ module Devise
|
|
|
62
102
|
end
|
|
63
103
|
|
|
64
104
|
result = if valid_password?(current_password)
|
|
65
|
-
|
|
105
|
+
update(params, *options)
|
|
66
106
|
else
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
107
|
+
assign_attributes(params, *options)
|
|
108
|
+
valid?
|
|
109
|
+
errors.add(:current_password, current_password.blank? ? :blank : :invalid)
|
|
70
110
|
false
|
|
71
111
|
end
|
|
72
112
|
|
|
@@ -87,29 +127,48 @@ module Devise
|
|
|
87
127
|
# end
|
|
88
128
|
#
|
|
89
129
|
def update_without_password(params, *options)
|
|
130
|
+
if options.present?
|
|
131
|
+
ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
|
|
132
|
+
[Devise] The second argument of `DatabaseAuthenticatable#update_without_password`
|
|
133
|
+
(`options`) is deprecated and it will be removed in the next major version.
|
|
134
|
+
It was added to support a feature deprecated in Rails 4, so you can safely remove it
|
|
135
|
+
from your code.
|
|
136
|
+
DEPRECATION
|
|
137
|
+
end
|
|
138
|
+
|
|
90
139
|
params.delete(:password)
|
|
91
140
|
params.delete(:password_confirmation)
|
|
92
141
|
|
|
93
|
-
result =
|
|
142
|
+
result = update(params, *options)
|
|
94
143
|
clean_up_passwords
|
|
95
144
|
result
|
|
96
145
|
end
|
|
97
146
|
|
|
98
147
|
# Destroy record when :current_password matches, otherwise returns
|
|
99
|
-
# error on :current_password. It also automatically rejects
|
|
148
|
+
# error on :current_password. It also automatically rejects
|
|
100
149
|
# :current_password if it is blank.
|
|
101
150
|
def destroy_with_password(current_password)
|
|
102
151
|
result = if valid_password?(current_password)
|
|
103
152
|
destroy
|
|
104
153
|
else
|
|
105
|
-
|
|
106
|
-
|
|
154
|
+
valid?
|
|
155
|
+
errors.add(:current_password, current_password.blank? ? :blank : :invalid)
|
|
107
156
|
false
|
|
108
157
|
end
|
|
109
158
|
|
|
110
159
|
result
|
|
111
160
|
end
|
|
112
161
|
|
|
162
|
+
# A callback initiated after successfully authenticating. This can be
|
|
163
|
+
# used to insert your own logic that is only run after the user successfully
|
|
164
|
+
# authenticates.
|
|
165
|
+
#
|
|
166
|
+
# Example:
|
|
167
|
+
#
|
|
168
|
+
# def after_database_authentication
|
|
169
|
+
# self.update_attribute(:invite_code, nil)
|
|
170
|
+
# end
|
|
171
|
+
#
|
|
113
172
|
def after_database_authentication
|
|
114
173
|
end
|
|
115
174
|
|
|
@@ -118,15 +177,56 @@ module Devise
|
|
|
118
177
|
encrypted_password[0,29] if encrypted_password
|
|
119
178
|
end
|
|
120
179
|
|
|
180
|
+
if Devise.activerecord51?
|
|
181
|
+
# Send notification to user when email changes.
|
|
182
|
+
def send_email_changed_notification
|
|
183
|
+
send_devise_notification(:email_changed, to: email_before_last_save)
|
|
184
|
+
end
|
|
185
|
+
else
|
|
186
|
+
# Send notification to user when email changes.
|
|
187
|
+
def send_email_changed_notification
|
|
188
|
+
send_devise_notification(:email_changed, to: email_was)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Send notification to user when password changes.
|
|
193
|
+
def send_password_change_notification
|
|
194
|
+
send_devise_notification(:password_change)
|
|
195
|
+
end
|
|
196
|
+
|
|
121
197
|
protected
|
|
122
198
|
|
|
123
|
-
#
|
|
199
|
+
# Hashes the password using bcrypt. Custom hash functions should override
|
|
200
|
+
# this method to apply their own algorithm.
|
|
201
|
+
#
|
|
202
|
+
# See https://github.com/heartcombo/devise-encryptable for examples
|
|
203
|
+
# of other hashing engines.
|
|
124
204
|
def password_digest(password)
|
|
125
|
-
::
|
|
205
|
+
Devise::Encryptor.digest(self.class, password)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
if Devise.activerecord51?
|
|
209
|
+
def send_email_changed_notification?
|
|
210
|
+
self.class.send_email_changed_notification && saved_change_to_email? && !@skip_email_changed_notification
|
|
211
|
+
end
|
|
212
|
+
else
|
|
213
|
+
def send_email_changed_notification?
|
|
214
|
+
self.class.send_email_changed_notification && email_changed? && !@skip_email_changed_notification
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
if Devise.activerecord51?
|
|
219
|
+
def send_password_change_notification?
|
|
220
|
+
self.class.send_password_change_notification && saved_change_to_encrypted_password? && !@skip_password_change_notification
|
|
221
|
+
end
|
|
222
|
+
else
|
|
223
|
+
def send_password_change_notification?
|
|
224
|
+
self.class.send_password_change_notification && encrypted_password_changed? && !@skip_password_change_notification
|
|
225
|
+
end
|
|
126
226
|
end
|
|
127
227
|
|
|
128
228
|
module ClassMethods
|
|
129
|
-
Devise::Models.config(self, :pepper, :stretches)
|
|
229
|
+
Devise::Models.config(self, :pepper, :stretches, :send_email_changed_notification, :send_password_change_notification)
|
|
130
230
|
|
|
131
231
|
# We assume this method already gets the sanitized values from the
|
|
132
232
|
# DatabaseAuthenticatable strategy. If you are using this method on
|