ultra-max-kit 0.0.1

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.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/devise-5.0.4/CHANGELOG.md +88 -0
  3. data/devise-5.0.4/MIT-LICENSE +21 -0
  4. data/devise-5.0.4/README.md +776 -0
  5. data/devise-5.0.4/app/controllers/devise/confirmations_controller.rb +54 -0
  6. data/devise-5.0.4/app/controllers/devise/omniauth_callbacks_controller.rb +36 -0
  7. data/devise-5.0.4/app/controllers/devise/passwords_controller.rb +88 -0
  8. data/devise-5.0.4/app/controllers/devise/registrations_controller.rb +169 -0
  9. data/devise-5.0.4/app/controllers/devise/sessions_controller.rb +83 -0
  10. data/devise-5.0.4/app/controllers/devise/unlocks_controller.rb +52 -0
  11. data/devise-5.0.4/app/controllers/devise_controller.rb +226 -0
  12. data/devise-5.0.4/app/helpers/devise_helper.rb +5 -0
  13. data/devise-5.0.4/app/mailers/devise/mailer.rb +30 -0
  14. data/devise-5.0.4/app/views/devise/confirmations/new.html.erb +16 -0
  15. data/devise-5.0.4/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  16. data/devise-5.0.4/app/views/devise/mailer/email_changed.html.erb +7 -0
  17. data/devise-5.0.4/app/views/devise/mailer/password_change.html.erb +3 -0
  18. data/devise-5.0.4/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  19. data/devise-5.0.4/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  20. data/devise-5.0.4/app/views/devise/passwords/edit.html.erb +25 -0
  21. data/devise-5.0.4/app/views/devise/passwords/new.html.erb +16 -0
  22. data/devise-5.0.4/app/views/devise/registrations/edit.html.erb +42 -0
  23. data/devise-5.0.4/app/views/devise/registrations/new.html.erb +29 -0
  24. data/devise-5.0.4/app/views/devise/sessions/new.html.erb +26 -0
  25. data/devise-5.0.4/app/views/devise/shared/_error_messages.html.erb +15 -0
  26. data/devise-5.0.4/app/views/devise/shared/_links.html.erb +25 -0
  27. data/devise-5.0.4/app/views/devise/unlocks/new.html.erb +16 -0
  28. data/devise-5.0.4/config/locales/en.yml +65 -0
  29. data/devise-5.0.4/lib/devise/controllers/helpers.rb +295 -0
  30. data/devise-5.0.4/lib/devise/controllers/rememberable.rb +56 -0
  31. data/devise-5.0.4/lib/devise/controllers/responder.rb +35 -0
  32. data/devise-5.0.4/lib/devise/controllers/scoped_views.rb +19 -0
  33. data/devise-5.0.4/lib/devise/controllers/sign_in_out.rb +106 -0
  34. data/devise-5.0.4/lib/devise/controllers/store_location.rb +76 -0
  35. data/devise-5.0.4/lib/devise/controllers/url_helpers.rb +69 -0
  36. data/devise-5.0.4/lib/devise/delegator.rb +18 -0
  37. data/devise-5.0.4/lib/devise/encryptor.rb +24 -0
  38. data/devise-5.0.4/lib/devise/failure_app.rb +287 -0
  39. data/devise-5.0.4/lib/devise/hooks/activatable.rb +12 -0
  40. data/devise-5.0.4/lib/devise/hooks/csrf_cleaner.rb +14 -0
  41. data/devise-5.0.4/lib/devise/hooks/forgetable.rb +11 -0
  42. data/devise-5.0.4/lib/devise/hooks/lockable.rb +9 -0
  43. data/devise-5.0.4/lib/devise/hooks/proxy.rb +23 -0
  44. data/devise-5.0.4/lib/devise/hooks/rememberable.rb +9 -0
  45. data/devise-5.0.4/lib/devise/hooks/timeoutable.rb +35 -0
  46. data/devise-5.0.4/lib/devise/hooks/trackable.rb +11 -0
  47. data/devise-5.0.4/lib/devise/mailers/helpers.rb +87 -0
  48. data/devise-5.0.4/lib/devise/mapping.rb +178 -0
  49. data/devise-5.0.4/lib/devise/models/authenticatable.rb +302 -0
  50. data/devise-5.0.4/lib/devise/models/confirmable.rb +371 -0
  51. data/devise-5.0.4/lib/devise/models/database_authenticatable.rb +204 -0
  52. data/devise-5.0.4/lib/devise/models/lockable.rb +214 -0
  53. data/devise-5.0.4/lib/devise/models/omniauthable.rb +29 -0
  54. data/devise-5.0.4/lib/devise/models/recoverable.rb +156 -0
  55. data/devise-5.0.4/lib/devise/models/registerable.rb +29 -0
  56. data/devise-5.0.4/lib/devise/models/rememberable.rb +158 -0
  57. data/devise-5.0.4/lib/devise/models/timeoutable.rb +45 -0
  58. data/devise-5.0.4/lib/devise/models/trackable.rb +51 -0
  59. data/devise-5.0.4/lib/devise/models/validatable.rb +70 -0
  60. data/devise-5.0.4/lib/devise/models.rb +122 -0
  61. data/devise-5.0.4/lib/devise/modules.rb +30 -0
  62. data/devise-5.0.4/lib/devise/omniauth/config.rb +47 -0
  63. data/devise-5.0.4/lib/devise/omniauth/url_helpers.rb +27 -0
  64. data/devise-5.0.4/lib/devise/omniauth.rb +27 -0
  65. data/devise-5.0.4/lib/devise/orm/active_record.rb +7 -0
  66. data/devise-5.0.4/lib/devise/orm/mongoid.rb +7 -0
  67. data/devise-5.0.4/lib/devise/orm.rb +80 -0
  68. data/devise-5.0.4/lib/devise/parameter_filter.rb +44 -0
  69. data/devise-5.0.4/lib/devise/parameter_sanitizer.rb +172 -0
  70. data/devise-5.0.4/lib/devise/rails/routes.rb +514 -0
  71. data/devise-5.0.4/lib/devise/rails/warden_compat.rb +15 -0
  72. data/devise-5.0.4/lib/devise/rails.rb +57 -0
  73. data/devise-5.0.4/lib/devise/strategies/authenticatable.rb +178 -0
  74. data/devise-5.0.4/lib/devise/strategies/base.rb +22 -0
  75. data/devise-5.0.4/lib/devise/strategies/database_authenticatable.rb +31 -0
  76. data/devise-5.0.4/lib/devise/strategies/rememberable.rb +67 -0
  77. data/devise-5.0.4/lib/devise/test/controller_helpers.rb +156 -0
  78. data/devise-5.0.4/lib/devise/test/integration_helpers.rb +63 -0
  79. data/devise-5.0.4/lib/devise/time_inflector.rb +16 -0
  80. data/devise-5.0.4/lib/devise/token_generator.rb +32 -0
  81. data/devise-5.0.4/lib/devise/version.rb +5 -0
  82. data/devise-5.0.4/lib/devise.rb +533 -0
  83. data/devise-5.0.4/lib/generators/active_record/devise_generator.rb +119 -0
  84. data/devise-5.0.4/lib/generators/active_record/templates/migration.rb +20 -0
  85. data/devise-5.0.4/lib/generators/active_record/templates/migration_existing.rb +27 -0
  86. data/devise-5.0.4/lib/generators/devise/controllers_generator.rb +46 -0
  87. data/devise-5.0.4/lib/generators/devise/devise_generator.rb +28 -0
  88. data/devise-5.0.4/lib/generators/devise/install_generator.rb +42 -0
  89. data/devise-5.0.4/lib/generators/devise/orm_helpers.rb +36 -0
  90. data/devise-5.0.4/lib/generators/devise/views_generator.rb +145 -0
  91. data/devise-5.0.4/lib/generators/mongoid/devise_generator.rb +57 -0
  92. data/devise-5.0.4/lib/generators/templates/README +36 -0
  93. data/devise-5.0.4/lib/generators/templates/controllers/README +14 -0
  94. data/devise-5.0.4/lib/generators/templates/controllers/confirmations_controller.rb +30 -0
  95. data/devise-5.0.4/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +30 -0
  96. data/devise-5.0.4/lib/generators/templates/controllers/passwords_controller.rb +34 -0
  97. data/devise-5.0.4/lib/generators/templates/controllers/registrations_controller.rb +62 -0
  98. data/devise-5.0.4/lib/generators/templates/controllers/sessions_controller.rb +27 -0
  99. data/devise-5.0.4/lib/generators/templates/controllers/unlocks_controller.rb +30 -0
  100. data/devise-5.0.4/lib/generators/templates/devise.rb +316 -0
  101. data/devise-5.0.4/lib/generators/templates/markerb/confirmation_instructions.markerb +5 -0
  102. data/devise-5.0.4/lib/generators/templates/markerb/email_changed.markerb +7 -0
  103. data/devise-5.0.4/lib/generators/templates/markerb/password_change.markerb +3 -0
  104. data/devise-5.0.4/lib/generators/templates/markerb/reset_password_instructions.markerb +8 -0
  105. data/devise-5.0.4/lib/generators/templates/markerb/unlock_instructions.markerb +7 -0
  106. data/devise-5.0.4/lib/generators/templates/simple_form_for/confirmations/new.html.erb +20 -0
  107. data/devise-5.0.4/lib/generators/templates/simple_form_for/passwords/edit.html.erb +27 -0
  108. data/devise-5.0.4/lib/generators/templates/simple_form_for/passwords/new.html.erb +18 -0
  109. data/devise-5.0.4/lib/generators/templates/simple_form_for/registrations/edit.html.erb +35 -0
  110. data/devise-5.0.4/lib/generators/templates/simple_form_for/registrations/new.html.erb +25 -0
  111. data/devise-5.0.4/lib/generators/templates/simple_form_for/sessions/new.html.erb +20 -0
  112. data/devise-5.0.4/lib/generators/templates/simple_form_for/unlocks/new.html.erb +19 -0
  113. data/ultra-max-kit.gemspec +12 -0
  114. metadata +153 -0
@@ -0,0 +1,371 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Devise
4
+ module Models
5
+ # Confirmable is responsible to verify if an account is already confirmed to
6
+ # sign in, and to send emails with confirmation instructions.
7
+ # Confirmation instructions are sent to the user email after creating a
8
+ # record and when manually requested by a new confirmation instruction request.
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
+ #
18
+ # == Options
19
+ #
20
+ # Confirmable adds the following options to +devise+:
21
+ #
22
+ # * +allow_unconfirmed_access_for+: the time you want to allow the user to access their account
23
+ # before confirming it. After this period, the user access is denied. You can
24
+ # use this to let your user access some features of your application without
25
+ # confirming the account, but blocking it after a certain period (ie 7 days).
26
+ # By default allow_unconfirmed_access_for is zero, it means users always have to confirm to sign in.
27
+ # * +reconfirmable+: requires any email changes to be confirmed (exactly the same way as
28
+ # initial account confirmation) to be applied. Requires additional unconfirmed_email
29
+ # db field to be set up (t.reconfirmable in migrations). Until confirmed, new email is
30
+ # stored in unconfirmed email column, and copied to email column on successful
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.
34
+ # * +confirm_within+: the time before a sent confirmation token becomes invalid.
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.
38
+ #
39
+ # == Examples
40
+ #
41
+ # User.find(1).confirm # returns true unless it's already confirmed
42
+ # User.find(1).confirmed? # true/false
43
+ # User.find(1).send_confirmation_instructions # manually send instructions
44
+ #
45
+ module Confirmable
46
+ extend ActiveSupport::Concern
47
+
48
+ included do
49
+ before_create :generate_confirmation_token, if: :confirmation_required?
50
+ after_create :skip_reconfirmation_in_callback!, if: :send_confirmation_notification?
51
+ if Devise::Orm.active_record?(self) # 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?
59
+ end
60
+
61
+ def initialize(*args, &block)
62
+ @bypass_confirmation_postpone = false
63
+ @skip_reconfirmation_in_callback = false
64
+ @reconfirmation_required = false
65
+ @skip_confirmation_notification = false
66
+ @raw_confirmation_token = nil
67
+ super
68
+ end
69
+
70
+ def self.required_fields(klass)
71
+ required_methods = [:confirmation_token, :confirmed_at, :confirmation_sent_at]
72
+ required_methods << :unconfirmed_email if klass.reconfirmable
73
+ required_methods
74
+ end
75
+
76
+ # Confirm a user by setting it's confirmed_at to actual time. If the user
77
+ # is already confirmed, add an error to email field. If the user is invalid
78
+ # add errors
79
+ def confirm(args = {})
80
+ pending_any_confirmation do
81
+ if confirmation_period_expired?
82
+ self.errors.add(:email, :confirmation_period_expired,
83
+ period: Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago))
84
+ return false
85
+ end
86
+
87
+ self.confirmed_at = Time.now.utc
88
+
89
+ saved = if pending_reconfirmation?
90
+ skip_reconfirmation!
91
+ self.email = unconfirmed_email
92
+ self.unconfirmed_email = nil
93
+
94
+ # We need to validate in such cases to enforce e-mail uniqueness
95
+ save(validate: true)
96
+ else
97
+ save(validate: args[:ensure_valid] == true)
98
+ end
99
+
100
+ after_confirmation if saved
101
+ saved
102
+ end
103
+ end
104
+
105
+ # Verifies whether a user is confirmed or not
106
+ def confirmed?
107
+ !!confirmed_at
108
+ end
109
+
110
+ def pending_reconfirmation?
111
+ self.class.reconfirmable && unconfirmed_email.present?
112
+ end
113
+
114
+ # Send confirmation instructions by email
115
+ def send_confirmation_instructions
116
+ unless @raw_confirmation_token
117
+ generate_confirmation_token!
118
+ end
119
+
120
+ opts = pending_reconfirmation? ? { to: unconfirmed_email } : { }
121
+ send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
122
+ end
123
+
124
+ def send_reconfirmation_instructions
125
+ @reconfirmation_required = false
126
+
127
+ unless @skip_confirmation_notification
128
+ send_confirmation_instructions
129
+ end
130
+ end
131
+
132
+ # Resend confirmation token.
133
+ # Regenerates the token if the period is expired.
134
+ def resend_confirmation_instructions
135
+ pending_any_confirmation do
136
+ send_confirmation_instructions
137
+ end
138
+ end
139
+
140
+ # Overwrites active_for_authentication? for confirmation
141
+ # by verifying whether a user is active to sign in or not. If the user
142
+ # is already confirmed, it should never be blocked. Otherwise we need to
143
+ # calculate if the confirm time has not expired for this user.
144
+ def active_for_authentication?
145
+ super && (!confirmation_required? || confirmed? || confirmation_period_valid?)
146
+ end
147
+
148
+ # The message to be shown if the account is inactive.
149
+ def inactive_message
150
+ !confirmed? ? :unconfirmed : super
151
+ end
152
+
153
+ # If you don't want confirmation to be sent on create, neither a code
154
+ # to be generated, call skip_confirmation!
155
+ def skip_confirmation!
156
+ self.confirmed_at = Time.now.utc
157
+ end
158
+
159
+ # Skips sending the confirmation/reconfirmation notification email after_create/after_update. Unlike
160
+ # #skip_confirmation!, record still requires confirmation.
161
+ def skip_confirmation_notification!
162
+ @skip_confirmation_notification = true
163
+ end
164
+
165
+ # If you don't want reconfirmation to be sent, neither a code
166
+ # to be generated, call skip_reconfirmation!
167
+ def skip_reconfirmation!
168
+ @bypass_confirmation_postpone = true
169
+ end
170
+
171
+ protected
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
+
179
+ # A callback method used to deliver confirmation
180
+ # instructions on creation. This can be overridden
181
+ # in models to map to a nice sign up e-mail.
182
+ def send_on_create_confirmation_instructions
183
+ send_confirmation_instructions
184
+ end
185
+
186
+ # Callback to overwrite if confirmation is required or not.
187
+ def confirmation_required?
188
+ !confirmed?
189
+ end
190
+
191
+ # Checks if the confirmation for the user is within the limit time.
192
+ # We do this by calculating if the difference between today and the
193
+ # confirmation sent date does not exceed the confirm in time configured.
194
+ # allow_unconfirmed_access_for is a model configuration, must always be an integer value.
195
+ #
196
+ # Example:
197
+ #
198
+ # # allow_unconfirmed_access_for = 1.day and confirmation_sent_at = today
199
+ # confirmation_period_valid? # returns true
200
+ #
201
+ # # allow_unconfirmed_access_for = 5.days and confirmation_sent_at = 4.days.ago
202
+ # confirmation_period_valid? # returns true
203
+ #
204
+ # # allow_unconfirmed_access_for = 5.days and confirmation_sent_at = 5.days.ago
205
+ # confirmation_period_valid? # returns false
206
+ #
207
+ # # allow_unconfirmed_access_for = 0.days
208
+ # confirmation_period_valid? # will always return false
209
+ #
210
+ # # allow_unconfirmed_access_for = nil
211
+ # confirmation_period_valid? # will always return true
212
+ #
213
+ def confirmation_period_valid?
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
218
+ end
219
+
220
+ # Checks if the user confirmation happens before the token becomes invalid
221
+ # Examples:
222
+ #
223
+ # # confirm_within = 3.days and confirmation_sent_at = 2.days.ago
224
+ # confirmation_period_expired? # returns false
225
+ #
226
+ # # confirm_within = 3.days and confirmation_sent_at = 4.days.ago
227
+ # confirmation_period_expired? # returns true
228
+ #
229
+ # # confirm_within = nil
230
+ # confirmation_period_expired? # will always return false
231
+ #
232
+ def confirmation_period_expired?
233
+ self.class.confirm_within && self.confirmation_sent_at && (Time.now.utc > self.confirmation_sent_at.utc + self.class.confirm_within)
234
+ end
235
+
236
+ # Checks whether the record requires any confirmation.
237
+ def pending_any_confirmation
238
+ if (!confirmed? || pending_reconfirmation?)
239
+ yield
240
+ else
241
+ self.errors.add(:email, :already_confirmed)
242
+ false
243
+ end
244
+ end
245
+
246
+ # Generates a new random token for confirmation, and stores
247
+ # the time this token is being generated in confirmation_sent_at
248
+ def generate_confirmation_token
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
255
+ end
256
+
257
+ def generate_confirmation_token!
258
+ generate_confirmation_token && save(validate: false)
259
+ end
260
+
261
+ def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
262
+ @reconfirmation_required = true
263
+ # Force unconfirmed_email to be updated, even if the value hasn't changed, to prevent a
264
+ # race condition which could allow an attacker to confirm an email they don't own. See #5783.
265
+ devise_unconfirmed_email_will_change!
266
+ self.unconfirmed_email = self.email
267
+ self.email = self.devise_email_in_database
268
+ self.confirmation_token = nil
269
+ generate_confirmation_token
270
+ end
271
+
272
+ def postpone_email_change?
273
+ postpone = self.class.reconfirmable &&
274
+ devise_will_save_change_to_email? &&
275
+ !@bypass_confirmation_postpone &&
276
+ self.email.present? &&
277
+ (!@skip_reconfirmation_in_callback || !self.devise_email_in_database.nil?)
278
+ @bypass_confirmation_postpone = false
279
+ postpone
280
+ end
281
+
282
+ def reconfirmation_required?
283
+ self.class.reconfirmable && @reconfirmation_required && (self.email.present? || self.unconfirmed_email.present?)
284
+ end
285
+
286
+ def send_confirmation_notification?
287
+ confirmation_required? && !@skip_confirmation_notification && self.email.present?
288
+ end
289
+
290
+ # With reconfirmable, notify the original email when the user first
291
+ # requests the email change, instead of when the change is confirmed.
292
+ def send_email_changed_notification?
293
+ if self.class.reconfirmable
294
+ self.class.send_email_changed_notification && reconfirmation_required?
295
+ else
296
+ super
297
+ end
298
+ end
299
+
300
+ # A callback initiated after successfully confirming. This can be
301
+ # used to insert your own logic that is only run after the user successfully
302
+ # confirms.
303
+ #
304
+ # Example:
305
+ #
306
+ # def after_confirmation
307
+ # self.update_attribute(:invite_code, nil)
308
+ # end
309
+ #
310
+ def after_confirmation
311
+ end
312
+
313
+ module ClassMethods
314
+ # Attempt to find a user by its email. If a record is found, send new
315
+ # confirmation instructions to it. If not, try searching for a user by unconfirmed_email
316
+ # field. If no user is found, returns a new user with an email not found error.
317
+ # Options must contain the user email
318
+ def send_confirmation_instructions(attributes = {})
319
+ confirmable = find_by_unconfirmed_email_with_errors(attributes) if reconfirmable
320
+ unless confirmable.try(:persisted?)
321
+ confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
322
+ end
323
+ confirmable.resend_confirmation_instructions if confirmable.persisted?
324
+ confirmable
325
+ end
326
+
327
+ # Find a user by its confirmation token and try to confirm it.
328
+ # If no user is found, returns a new user with an error.
329
+ # If the user is already confirmed, create an error for the user
330
+ # Options must have the confirmation_token
331
+ def confirm_by_token(confirmation_token)
332
+ # When the `confirmation_token` parameter is blank, if there are any users with a blank
333
+ # `confirmation_token` in the database, the first one would be confirmed here.
334
+ # The error is being manually added here to ensure no users are confirmed by mistake.
335
+ # This was done in the model for convenience, since validation errors are automatically
336
+ # displayed in the view.
337
+ if confirmation_token.blank?
338
+ confirmable = new
339
+ confirmable.errors.add(:confirmation_token, :blank)
340
+ return confirmable
341
+ end
342
+
343
+ confirmable = find_first_by_auth_conditions(confirmation_token: confirmation_token)
344
+
345
+ unless confirmable
346
+ confirmation_digest = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
347
+ confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_digest)
348
+ end
349
+
350
+ # TODO: replace above lines with
351
+ # confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
352
+ # after enough time has passed that Devise clients do not use digested tokens
353
+
354
+ confirmable.confirm if confirmable.persisted?
355
+ confirmable
356
+ end
357
+
358
+ # Find a record for confirmation by unconfirmed email field
359
+ def find_by_unconfirmed_email_with_errors(attributes = {})
360
+ attributes = attributes.slice(*confirmation_keys).permit!.to_h if attributes.respond_to? :permit
361
+ unconfirmed_required_attributes = confirmation_keys.map { |k| k == :email ? :unconfirmed_email : k }
362
+ unconfirmed_attributes = attributes.symbolize_keys
363
+ unconfirmed_attributes[:unconfirmed_email] = unconfirmed_attributes.delete(:email)
364
+ find_or_initialize_with_errors(unconfirmed_required_attributes, unconfirmed_attributes, :not_found)
365
+ end
366
+
367
+ Devise::Models.config(self, :allow_unconfirmed_access_for, :confirmation_keys, :reconfirmable, :confirm_within)
368
+ end
369
+ end
370
+ end
371
+ end
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'devise/strategies/database_authenticatable'
4
+
5
+ module Devise
6
+ module Models
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.
13
+ #
14
+ # == Options
15
+ #
16
+ # DatabaseAuthenticatable adds the following options to +devise+:
17
+ #
18
+ # * +pepper+: a random string used to provide a more secure hash. Use
19
+ # `rails secret` to generate new keys.
20
+ #
21
+ # * +stretches+: the cost given to bcrypt.
22
+ #
23
+ # * +send_email_changed_notification+: notify original email when it changes.
24
+ #
25
+ # * +send_password_change_notification+: notify email when password changes.
26
+ #
27
+ # == Examples
28
+ #
29
+ # User.find(1).valid_password?('password123') # returns true/false
30
+ #
31
+ module DatabaseAuthenticatable
32
+ extend ActiveSupport::Concern
33
+
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
+
38
+ attr_reader :password, :current_password
39
+ attr_accessor :password_confirmation
40
+ end
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
+
58
+ def self.required_fields(klass)
59
+ [:encrypted_password] + klass.authentication_keys
60
+ end
61
+
62
+ # Generates a hashed password based on the given value.
63
+ # For legacy reasons, we use `encrypted_password` to store
64
+ # the hashed password.
65
+ def password=(new_password)
66
+ @password = new_password
67
+ self.encrypted_password = password_digest(@password) if @password.present?
68
+ end
69
+
70
+ # Verifies whether a password (ie from sign in) is the user password.
71
+ def valid_password?(password)
72
+ Devise::Encryptor.compare(self.class, encrypted_password, password)
73
+ end
74
+
75
+ # Set password and password confirmation to nil
76
+ def clean_up_passwords
77
+ self.password = self.password_confirmation = nil
78
+ end
79
+
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.
87
+ def update_with_password(params)
88
+ current_password = params.delete(:current_password)
89
+
90
+ if params[:password].blank?
91
+ params.delete(:password)
92
+ params.delete(:password_confirmation) if params[:password_confirmation].blank?
93
+ end
94
+
95
+ result = if valid_password?(current_password)
96
+ update(params)
97
+ else
98
+ assign_attributes(params)
99
+ valid?
100
+ errors.add(:current_password, current_password.blank? ? :blank : :invalid)
101
+ false
102
+ end
103
+
104
+ clean_up_passwords
105
+ result
106
+ end
107
+
108
+ # Updates record attributes without asking for the current password.
109
+ # Never allows a change to the current password. If you are using this
110
+ # method, you should probably override this method to protect other
111
+ # attributes you would not like to be updated without a password.
112
+ #
113
+ # Example:
114
+ #
115
+ # def update_without_password(params)
116
+ # params.delete(:email)
117
+ # super(params)
118
+ # end
119
+ #
120
+ def update_without_password(params)
121
+ params.delete(:password)
122
+ params.delete(:password_confirmation)
123
+
124
+ result = update(params)
125
+ clean_up_passwords
126
+ result
127
+ end
128
+
129
+ # Destroy record when :current_password matches, otherwise returns
130
+ # error on :current_password. It also automatically rejects
131
+ # :current_password if it is blank.
132
+ def destroy_with_password(current_password)
133
+ result = if valid_password?(current_password)
134
+ destroy
135
+ else
136
+ valid?
137
+ errors.add(:current_password, current_password.blank? ? :blank : :invalid)
138
+ false
139
+ end
140
+
141
+ result
142
+ end
143
+
144
+ # A callback initiated after successfully authenticating. This can be
145
+ # used to insert your own logic that is only run after the user successfully
146
+ # authenticates.
147
+ #
148
+ # Example:
149
+ #
150
+ # def after_database_authentication
151
+ # self.update_attribute(:invite_code, nil)
152
+ # end
153
+ #
154
+ def after_database_authentication
155
+ end
156
+
157
+ # A reliable way to expose the salt regardless of the implementation.
158
+ def authenticatable_salt
159
+ encrypted_password[0,29] if encrypted_password
160
+ end
161
+
162
+ # Send notification to user when email changes.
163
+ def send_email_changed_notification
164
+ send_devise_notification(:email_changed, to: devise_email_before_last_save)
165
+ end
166
+
167
+ # Send notification to user when password changes.
168
+ def send_password_change_notification
169
+ send_devise_notification(:password_change)
170
+ end
171
+
172
+ protected
173
+
174
+ # Hashes the password using bcrypt. Custom hash functions should override
175
+ # this method to apply their own algorithm.
176
+ #
177
+ # See https://github.com/heartcombo/devise-encryptable for examples
178
+ # of other hashing engines.
179
+ def password_digest(password)
180
+ Devise::Encryptor.digest(self.class, password)
181
+ end
182
+
183
+ def send_email_changed_notification?
184
+ self.class.send_email_changed_notification && devise_saved_change_to_email? && !@skip_email_changed_notification
185
+ end
186
+
187
+ def send_password_change_notification?
188
+ self.class.send_password_change_notification && devise_saved_change_to_encrypted_password? && !@skip_password_change_notification
189
+ end
190
+
191
+ module ClassMethods
192
+ Devise::Models.config(self, :pepper, :stretches, :send_email_changed_notification, :send_password_change_notification)
193
+
194
+ # We assume this method already gets the sanitized values from the
195
+ # DatabaseAuthenticatable strategy. If you are using this method on
196
+ # your own, be sure to sanitize the conditions hash to only include
197
+ # the proper fields.
198
+ def find_for_database_authentication(conditions)
199
+ find_for_authentication(conditions)
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end