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,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "devise/hooks/lockable"
4
+
5
+ module Devise
6
+ module Models
7
+ # Handles blocking a user access after a certain number of attempts.
8
+ # Lockable accepts two different strategies to unlock a user after it's
9
+ # blocked: email and time. The former will send an email to the user when
10
+ # the lock happens, containing a link to unlock its account. The second
11
+ # will unlock the user automatically after some configured time (ie 2.hours).
12
+ # It's also possible to set up lockable to use both email and time strategies.
13
+ #
14
+ # == Options
15
+ #
16
+ # Lockable adds the following options to +devise+:
17
+ #
18
+ # * +maximum_attempts+: how many attempts should be accepted before blocking the user.
19
+ # * +lock_strategy+: lock the user account by :failed_attempts or :none.
20
+ # * +unlock_strategy+: unlock the user account by :time, :email, :both or :none.
21
+ # * +unlock_in+: the time you want to unlock the user after lock happens. Only available when unlock_strategy is :time or :both.
22
+ # * +unlock_keys+: the keys you want to use when locking and unlocking an account
23
+ #
24
+ module Lockable
25
+ extend ActiveSupport::Concern
26
+
27
+ delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, to: "self.class"
28
+
29
+ def self.required_fields(klass)
30
+ attributes = []
31
+ attributes << :failed_attempts if klass.lock_strategy_enabled?(:failed_attempts)
32
+ attributes << :locked_at if klass.unlock_strategy_enabled?(:time)
33
+ attributes << :unlock_token if klass.unlock_strategy_enabled?(:email)
34
+
35
+ attributes
36
+ end
37
+
38
+ # Lock a user setting its locked_at to actual time.
39
+ # * +opts+: Hash options if you don't want to send email
40
+ # when you lock access, you could pass the next hash
41
+ # `{ send_instructions: false } as option`.
42
+ def lock_access!(opts = { })
43
+ self.locked_at = Time.now.utc
44
+
45
+ if unlock_strategy_enabled?(:email) && opts.fetch(:send_instructions, true)
46
+ send_unlock_instructions
47
+ else
48
+ save(validate: false)
49
+ end
50
+ end
51
+
52
+ # Unlock a user by cleaning locked_at and failed_attempts.
53
+ def unlock_access!
54
+ self.locked_at = nil
55
+ self.failed_attempts = 0 if respond_to?(:failed_attempts=)
56
+ self.unlock_token = nil if respond_to?(:unlock_token=)
57
+ save(validate: false)
58
+ end
59
+
60
+ # Resets failed attempts counter to 0.
61
+ def reset_failed_attempts!
62
+ if respond_to?(:failed_attempts) && !failed_attempts.to_i.zero?
63
+ self.failed_attempts = 0
64
+ save(validate: false)
65
+ end
66
+ end
67
+
68
+ # Verifies whether a user is locked or not.
69
+ def access_locked?
70
+ !!locked_at && !lock_expired?
71
+ end
72
+
73
+ # Send unlock instructions by email
74
+ def send_unlock_instructions
75
+ raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
76
+ self.unlock_token = enc
77
+ save(validate: false)
78
+ send_devise_notification(:unlock_instructions, raw, {})
79
+ raw
80
+ end
81
+
82
+ # Resend the unlock instructions if the user is locked.
83
+ def resend_unlock_instructions
84
+ if_access_locked { send_unlock_instructions }
85
+ end
86
+
87
+ # Overwrites active_for_authentication? from Devise::Models::Authenticatable for locking purposes
88
+ # by verifying whether a user is active to sign in or not based on locked?
89
+ def active_for_authentication?
90
+ super && !access_locked?
91
+ end
92
+
93
+ # Overwrites invalid_message from Devise::Models::Authenticatable to define
94
+ # the correct reason for blocking the sign in.
95
+ def inactive_message
96
+ access_locked? ? :locked : super
97
+ end
98
+
99
+ # Overwrites valid_for_authentication? from Devise::Models::Authenticatable
100
+ # for verifying whether a user is allowed to sign in or not. If the user
101
+ # is locked, it should never be allowed.
102
+ def valid_for_authentication?
103
+ return super unless persisted? && lock_strategy_enabled?(:failed_attempts)
104
+
105
+ # Unlock the user if the lock is expired, no matter
106
+ # if the user can login or not (wrong password, etc)
107
+ unlock_access! if lock_expired?
108
+
109
+ if super && !access_locked?
110
+ true
111
+ else
112
+ increment_failed_attempts
113
+ if attempts_exceeded?
114
+ lock_access! unless access_locked?
115
+ else
116
+ save(validate: false)
117
+ end
118
+ false
119
+ end
120
+ end
121
+
122
+ def increment_failed_attempts
123
+ self.class.increment_counter(:failed_attempts, id)
124
+ reload
125
+ end
126
+
127
+ def unauthenticated_message
128
+ # If set to paranoid mode, do not show the locked message because it
129
+ # leaks the existence of an account.
130
+ if Devise.paranoid
131
+ super
132
+ elsif access_locked? || (lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?)
133
+ :locked
134
+ elsif lock_strategy_enabled?(:failed_attempts) && last_attempt? && self.class.last_attempt_warning
135
+ :last_attempt
136
+ else
137
+ super
138
+ end
139
+ end
140
+
141
+ protected
142
+
143
+ def attempts_exceeded?
144
+ self.failed_attempts >= self.class.maximum_attempts
145
+ end
146
+
147
+ def last_attempt?
148
+ self.failed_attempts == self.class.maximum_attempts - 1
149
+ end
150
+
151
+ # Tells if the lock is expired if :time unlock strategy is active
152
+ def lock_expired?
153
+ if unlock_strategy_enabled?(:time)
154
+ locked_at && locked_at < self.class.unlock_in.ago
155
+ else
156
+ false
157
+ end
158
+ end
159
+
160
+ # Checks whether the record is locked or not, yielding to the block
161
+ # if it's locked, otherwise adds an error to email.
162
+ def if_access_locked
163
+ if access_locked?
164
+ yield
165
+ else
166
+ self.errors.add(Devise.unlock_keys.first, :not_locked)
167
+ false
168
+ end
169
+ end
170
+
171
+ module ClassMethods
172
+ # List of strategies that are enabled/supported if :both is used.
173
+ BOTH_STRATEGIES = [:time, :email]
174
+
175
+ # Attempt to find a user by its unlock keys. If a record is found, send new
176
+ # unlock instructions to it. If not user is found, returns a new user
177
+ # with an email not found error.
178
+ # Options must contain the user's unlock keys
179
+ def send_unlock_instructions(attributes = {})
180
+ lockable = find_or_initialize_with_errors(unlock_keys, attributes, :not_found)
181
+ lockable.resend_unlock_instructions if lockable.persisted?
182
+ lockable
183
+ end
184
+
185
+ # Find a user by its unlock token and try to unlock it.
186
+ # If no user is found, returns a new user with an error.
187
+ # If the user is not locked, creates an error for the user
188
+ # Options must have the unlock_token
189
+ def unlock_access_by_token(unlock_token)
190
+ original_token = unlock_token
191
+ unlock_token = Devise.token_generator.digest(self, :unlock_token, unlock_token)
192
+
193
+ lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token)
194
+ lockable.unlock_access! if lockable.persisted?
195
+ lockable.unlock_token = original_token
196
+ lockable
197
+ end
198
+
199
+ # Is the unlock enabled for the given unlock strategy?
200
+ def unlock_strategy_enabled?(strategy)
201
+ self.unlock_strategy == strategy ||
202
+ (self.unlock_strategy == :both && BOTH_STRATEGIES.include?(strategy))
203
+ end
204
+
205
+ # Is the lock enabled for the given lock strategy?
206
+ def lock_strategy_enabled?(strategy)
207
+ self.lock_strategy == strategy
208
+ end
209
+
210
+ Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys, :last_attempt_warning)
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'devise/omniauth'
4
+
5
+ module Devise
6
+ module Models
7
+ # Adds OmniAuth support to your model.
8
+ #
9
+ # == Options
10
+ #
11
+ # Oauthable adds the following options to +devise+:
12
+ #
13
+ # * +omniauth_providers+: Which providers are available to this model. It expects an array:
14
+ #
15
+ # devise :database_authenticatable, :omniauthable, omniauth_providers: [:twitter]
16
+ #
17
+ module Omniauthable
18
+ extend ActiveSupport::Concern
19
+
20
+ def self.required_fields(klass)
21
+ []
22
+ end
23
+
24
+ module ClassMethods
25
+ Devise::Models.config(self, :omniauth_providers)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Devise
4
+ module Models
5
+
6
+ # Recoverable takes care of resetting the user password and send reset instructions.
7
+ #
8
+ # ==Options
9
+ #
10
+ # Recoverable adds the following options to +devise+:
11
+ #
12
+ # * +reset_password_keys+: the keys you want to use when recovering the password for an account
13
+ # * +reset_password_within+: the time period within which the password must be reset or the token expires.
14
+ # * +sign_in_after_reset_password+: whether or not to sign in the user automatically after a password reset.
15
+ #
16
+ # == Examples
17
+ #
18
+ # # resets the user password and save the record, true if valid passwords are given, otherwise false
19
+ # User.find(1).reset_password('password123', 'password123')
20
+ #
21
+ # # creates a new token and send it with instructions about how to reset the password
22
+ # User.find(1).send_reset_password_instructions
23
+ #
24
+ module Recoverable
25
+ extend ActiveSupport::Concern
26
+
27
+ def self.required_fields(klass)
28
+ [:reset_password_sent_at, :reset_password_token]
29
+ end
30
+
31
+ included do
32
+ before_update :clear_reset_password_token, if: :clear_reset_password_token?
33
+ end
34
+
35
+ # Update password saving the record and clearing token. Returns true if
36
+ # the passwords are valid and the record was saved, false otherwise.
37
+ def reset_password(new_password, new_password_confirmation)
38
+ if new_password.present?
39
+ self.password = new_password
40
+ self.password_confirmation = new_password_confirmation
41
+ save
42
+ else
43
+ errors.add(:password, :blank)
44
+ false
45
+ end
46
+ end
47
+
48
+ # Resets reset password token and send reset password instructions by email.
49
+ # Returns the token sent in the e-mail.
50
+ def send_reset_password_instructions
51
+ token = set_reset_password_token
52
+ send_reset_password_instructions_notification(token)
53
+
54
+ token
55
+ end
56
+
57
+ # Checks if the reset password token sent is within the limit time.
58
+ # We do this by calculating if the difference between today and the
59
+ # sending date does not exceed the confirm in time configured.
60
+ # Returns true if the resource is not responding to reset_password_sent_at at all.
61
+ # reset_password_within is a model configuration, must always be an integer value.
62
+ #
63
+ # Example:
64
+ #
65
+ # # reset_password_within = 1.day and reset_password_sent_at = today
66
+ # reset_password_period_valid? # returns true
67
+ #
68
+ # # reset_password_within = 5.days and reset_password_sent_at = 4.days.ago
69
+ # reset_password_period_valid? # returns true
70
+ #
71
+ # # reset_password_within = 5.days and reset_password_sent_at = 5.days.ago
72
+ # reset_password_period_valid? # returns false
73
+ #
74
+ # # reset_password_within = 0.days
75
+ # reset_password_period_valid? # will always return false
76
+ #
77
+ def reset_password_period_valid?
78
+ reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago.utc
79
+ end
80
+
81
+ protected
82
+
83
+ # Removes reset_password token
84
+ def clear_reset_password_token
85
+ self.reset_password_token = nil
86
+ self.reset_password_sent_at = nil
87
+ end
88
+
89
+ def set_reset_password_token
90
+ raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
91
+
92
+ self.reset_password_token = enc
93
+ self.reset_password_sent_at = Time.now.utc
94
+ save(validate: false)
95
+ raw
96
+ end
97
+
98
+ def send_reset_password_instructions_notification(token)
99
+ send_devise_notification(:reset_password_instructions, token, {})
100
+ end
101
+
102
+ def clear_reset_password_token?
103
+ encrypted_password_changed = devise_respond_to_and_will_save_change_to_attribute?(:encrypted_password)
104
+ authentication_keys_changed = self.class.authentication_keys.any? do |attribute|
105
+ devise_respond_to_and_will_save_change_to_attribute?(attribute)
106
+ end
107
+
108
+ authentication_keys_changed || encrypted_password_changed
109
+ end
110
+
111
+ module ClassMethods
112
+ # Attempt to find a user by password reset token. If a user is found, return it
113
+ # If a user is not found, return nil
114
+ def with_reset_password_token(token)
115
+ reset_password_token = Devise.token_generator.digest(self, :reset_password_token, token)
116
+ to_adapter.find_first(reset_password_token: reset_password_token)
117
+ end
118
+
119
+ # Attempt to find a user by its email. If a record is found, send new
120
+ # password instructions to it. If user is not found, returns a new user
121
+ # with an email not found error.
122
+ # Attributes must contain the user's email
123
+ def send_reset_password_instructions(attributes = {})
124
+ recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
125
+ recoverable.send_reset_password_instructions if recoverable.persisted?
126
+ recoverable
127
+ end
128
+
129
+ # Attempt to find a user by its reset_password_token to reset its
130
+ # password. If a user is found and token is still valid, reset its password and automatically
131
+ # try saving the record. If not user is found, returns a new user
132
+ # containing an error in reset_password_token attribute.
133
+ # Attributes must contain reset_password_token, password and confirmation
134
+ def reset_password_by_token(attributes = {})
135
+ original_token = attributes[:reset_password_token]
136
+ reset_password_token = Devise.token_generator.digest(self, :reset_password_token, original_token)
137
+
138
+ recoverable = find_or_initialize_with_error_by(:reset_password_token, reset_password_token)
139
+
140
+ if recoverable.persisted?
141
+ if recoverable.reset_password_period_valid?
142
+ recoverable.reset_password(attributes[:password], attributes[:password_confirmation])
143
+ else
144
+ recoverable.errors.add(:reset_password_token, :expired)
145
+ end
146
+ end
147
+
148
+ recoverable.reset_password_token = original_token if recoverable.reset_password_token.present?
149
+ recoverable
150
+ end
151
+
152
+ Devise::Models.config(self, :reset_password_keys, :reset_password_within, :sign_in_after_reset_password)
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Devise
4
+ module Models
5
+ # Registerable is responsible for everything related to registering a new
6
+ # resource (ie user sign up).
7
+ module Registerable
8
+ extend ActiveSupport::Concern
9
+
10
+ def self.required_fields(klass)
11
+ []
12
+ end
13
+
14
+ module ClassMethods
15
+ # A convenience method that receives both parameters and session to
16
+ # initialize a user. This can be used by OAuth, for example, to send
17
+ # in the user token and be stored on initialization.
18
+ #
19
+ # By default discards all information sent by the session by calling
20
+ # new with params.
21
+ def new_with_session(params, session)
22
+ new(params)
23
+ end
24
+
25
+ Devise::Models.config(self, :sign_in_after_change_password)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'devise/strategies/rememberable'
4
+ require 'devise/hooks/rememberable'
5
+ require 'devise/hooks/forgetable'
6
+
7
+ module Devise
8
+ module Models
9
+ # Rememberable manages generating and clearing token for remembering the user
10
+ # from a saved cookie. Rememberable also has utility methods for dealing
11
+ # with serializing the user into the cookie and back from the cookie, trying
12
+ # to lookup the record based on the saved information.
13
+ # You probably wouldn't use rememberable methods directly, they are used
14
+ # mostly internally for handling the remember token.
15
+ #
16
+ # == Options
17
+ #
18
+ # Rememberable adds the following options to +devise+:
19
+ #
20
+ # * +remember_for+: the time you want the user will be remembered without
21
+ # asking for credentials. After this time the user will be blocked and
22
+ # will have to enter their credentials again. This configuration is also
23
+ # used to calculate the expires time for the cookie created to remember
24
+ # the user. By default remember_for is 2.weeks.
25
+ #
26
+ # * +extend_remember_period+: if true, extends the user's remember period
27
+ # when remembered via cookie. False by default.
28
+ #
29
+ # * +rememberable_options+: configuration options passed to the created cookie.
30
+ #
31
+ # == Examples
32
+ #
33
+ # User.find(1).remember_me! # regenerating the token
34
+ # User.find(1).forget_me! # clearing the token
35
+ #
36
+ # # generating info to put into cookies
37
+ # User.serialize_into_cookie(user)
38
+ #
39
+ # # lookup the user based on the incoming cookie information
40
+ # User.serialize_from_cookie(cookie_string)
41
+ module Rememberable
42
+ extend ActiveSupport::Concern
43
+
44
+ attr_accessor :remember_me
45
+
46
+ def self.required_fields(klass)
47
+ [:remember_created_at]
48
+ end
49
+
50
+ def remember_me!
51
+ self.remember_token ||= self.class.remember_token if respond_to?(:remember_token)
52
+ self.remember_created_at ||= Time.now.utc
53
+ save(validate: false) if self.changed?
54
+ end
55
+
56
+ # If the record is persisted, remove the remember token (but only if
57
+ # it exists), and save the record without validations.
58
+ def forget_me!
59
+ return unless persisted?
60
+ self.remember_token = nil if respond_to?(:remember_token)
61
+ self.remember_created_at = nil if self.class.expire_all_remember_me_on_sign_out
62
+ save(validate: false)
63
+ end
64
+
65
+ def remember_expires_at
66
+ self.class.remember_for.from_now
67
+ end
68
+
69
+ def extend_remember_period
70
+ self.class.extend_remember_period
71
+ end
72
+
73
+ def rememberable_value
74
+ if respond_to?(:remember_token)
75
+ remember_token
76
+ elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt.presence)
77
+ salt
78
+ else
79
+ raise "authenticatable_salt returned nil for the #{self.class.name} model. " \
80
+ "In order to use rememberable, you must ensure a password is always set " \
81
+ "or have a remember_token column in your model or implement your own " \
82
+ "rememberable_value in the model with custom logic."
83
+ end
84
+ end
85
+
86
+ def rememberable_options
87
+ self.class.rememberable_options
88
+ end
89
+
90
+ # A callback initiated after successfully being remembered. This can be
91
+ # used to insert your own logic that is only run after the user is
92
+ # remembered.
93
+ #
94
+ # Example:
95
+ #
96
+ # def after_remembered
97
+ # self.update_attribute(:invite_code, nil)
98
+ # end
99
+ #
100
+ def after_remembered
101
+ end
102
+
103
+ def remember_me?(token, generated_at)
104
+ # TODO: Normalize the JSON type coercion along with the Timeoutable hook
105
+ # in a single place https://github.com/heartcombo/devise/blob/ffe9d6d406e79108cf32a2c6a1d0b3828849c40b/lib/devise/hooks/timeoutable.rb#L14-L18
106
+ if generated_at.is_a?(String)
107
+ generated_at = time_from_json(generated_at)
108
+ end
109
+
110
+ # The token is only valid if:
111
+ # 1. we have a date
112
+ # 2. the current time does not pass the expiry period
113
+ # 3. the record has a remember_created_at date
114
+ # 4. the token date is bigger than the remember_created_at
115
+ # 5. the token matches
116
+ generated_at.is_a?(Time) &&
117
+ (self.class.remember_for.ago < generated_at) &&
118
+ (generated_at > (remember_created_at || Time.now).utc) &&
119
+ Devise.secure_compare(rememberable_value, token)
120
+ end
121
+
122
+ private
123
+
124
+ def time_from_json(value)
125
+ if value =~ /\A\d+\.\d+\Z/
126
+ Time.at(value.to_f)
127
+ else
128
+ Time.parse(value) rescue nil
129
+ end
130
+ end
131
+
132
+ module ClassMethods
133
+ # Create the cookie key using the record id and remember_token
134
+ def serialize_into_cookie(record)
135
+ [record.to_key, record.rememberable_value, Time.now.utc.to_f.to_s]
136
+ end
137
+
138
+ # Recreate the user based on the stored cookie
139
+ def serialize_from_cookie(*args)
140
+ id, token, generated_at = *args
141
+
142
+ record = to_adapter.get(id)
143
+ record if record && record.remember_me?(token, generated_at)
144
+ end
145
+
146
+ # Generate a token checking if one does not already exist in the database.
147
+ def remember_token #:nodoc:
148
+ loop do
149
+ token = Devise.friendly_token
150
+ break token unless to_adapter.find_first({ remember_token: token })
151
+ end
152
+ end
153
+
154
+ Devise::Models.config(self, :remember_for, :extend_remember_period, :rememberable_options, :expire_all_remember_me_on_sign_out)
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'devise/hooks/timeoutable'
4
+
5
+ module Devise
6
+ module Models
7
+ # Timeoutable takes care of verifying whether a user session has already
8
+ # expired or not. When a session expires after the configured time, the user
9
+ # will be asked for credentials again, it means, they will be redirected
10
+ # to the sign in page.
11
+ #
12
+ # == Options
13
+ #
14
+ # Timeoutable adds the following options to +devise+:
15
+ #
16
+ # * +timeout_in+: the interval to timeout the user session without activity.
17
+ #
18
+ # == Examples
19
+ #
20
+ # user.timedout?(30.minutes.ago)
21
+ #
22
+ module Timeoutable
23
+ extend ActiveSupport::Concern
24
+
25
+ def self.required_fields(klass)
26
+ []
27
+ end
28
+
29
+ # Checks whether the user session has expired based on configured time.
30
+ def timedout?(last_access)
31
+ !timeout_in.nil? && last_access && last_access <= timeout_in.ago
32
+ end
33
+
34
+ def timeout_in
35
+ self.class.timeout_in
36
+ end
37
+
38
+ private
39
+
40
+ module ClassMethods
41
+ Devise::Models.config(self, :timeout_in)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'devise/hooks/trackable'
4
+
5
+ module Devise
6
+ module Models
7
+ # Track information about your user sign in. It tracks the following columns:
8
+ #
9
+ # * sign_in_count - Increased every time a sign in is made (by form, openid, oauth)
10
+ # * current_sign_in_at - A timestamp updated when the user signs in
11
+ # * last_sign_in_at - Holds the timestamp of the previous sign in
12
+ # * current_sign_in_ip - The remote ip updated when the user sign in
13
+ # * last_sign_in_ip - Holds the remote ip of the previous sign in
14
+ #
15
+ module Trackable
16
+ def self.required_fields(klass)
17
+ [:current_sign_in_at, :current_sign_in_ip, :last_sign_in_at, :last_sign_in_ip, :sign_in_count]
18
+ end
19
+
20
+ def update_tracked_fields(request)
21
+ old_current, new_current = self.current_sign_in_at, Time.now.utc
22
+ self.last_sign_in_at = old_current || new_current
23
+ self.current_sign_in_at = new_current
24
+
25
+ old_current, new_current = self.current_sign_in_ip, extract_ip_from(request)
26
+ self.last_sign_in_ip = old_current || new_current
27
+ self.current_sign_in_ip = new_current
28
+
29
+ self.sign_in_count ||= 0
30
+ self.sign_in_count += 1
31
+ end
32
+
33
+ def update_tracked_fields!(request)
34
+ # We have to check if the user is already persisted before running
35
+ # `save` here because invalid users can be saved if we don't.
36
+ # See https://github.com/heartcombo/devise/issues/4673 for more details.
37
+ return if new_record?
38
+
39
+ update_tracked_fields(request)
40
+ save(validate: false)
41
+ end
42
+
43
+ protected
44
+
45
+ def extract_ip_from(request)
46
+ request.remote_ip
47
+ end
48
+
49
+ end
50
+ end
51
+ end