devise 4.7.1 → 4.9.2
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 +4 -4
- data/CHANGELOG.md +80 -3
- data/MIT-LICENSE +2 -1
- data/README.md +100 -65
- data/app/controllers/devise/confirmations_controller.rb +1 -0
- data/app/controllers/devise/passwords_controller.rb +2 -2
- data/app/controllers/devise/registrations_controller.rb +1 -1
- data/app/controllers/devise/sessions_controller.rb +1 -1
- data/app/controllers/devise/unlocks_controller.rb +1 -0
- data/app/controllers/devise_controller.rb +3 -2
- data/app/helpers/devise_helper.rb +18 -6
- data/app/mailers/devise/mailer.rb +5 -5
- data/app/views/devise/registrations/edit.html.erb +1 -1
- data/app/views/devise/shared/_error_messages.html.erb +1 -1
- data/app/views/devise/shared/_links.html.erb +1 -1
- data/config/locales/en.yml +2 -2
- data/lib/devise/controllers/helpers.rb +7 -7
- data/lib/devise/controllers/responder.rb +35 -0
- data/lib/devise/controllers/sign_in_out.rb +6 -4
- data/lib/devise/controllers/url_helpers.rb +1 -1
- data/lib/devise/failure_app.rb +8 -5
- data/lib/devise/hooks/csrf_cleaner.rb +6 -1
- data/lib/devise/hooks/lockable.rb +2 -5
- data/lib/devise/hooks/timeoutable.rb +2 -2
- data/lib/devise/mapping.rb +1 -1
- data/lib/devise/models/authenticatable.rb +13 -8
- data/lib/devise/models/confirmable.rb +18 -39
- data/lib/devise/models/database_authenticatable.rb +14 -29
- data/lib/devise/models/lockable.rb +11 -3
- data/lib/devise/models/omniauthable.rb +2 -2
- data/lib/devise/models/recoverable.rb +8 -19
- data/lib/devise/models/rememberable.rb +2 -2
- data/lib/devise/models/timeoutable.rb +1 -1
- data/lib/devise/models/trackable.rb +1 -1
- data/lib/devise/models/validatable.rb +4 -9
- data/lib/devise/models.rb +1 -0
- data/lib/devise/omniauth.rb +2 -5
- data/lib/devise/orm.rb +71 -0
- data/lib/devise/rails/deprecated_constant_accessor.rb +39 -0
- data/lib/devise/rails/routes.rb +4 -4
- data/lib/devise/test/controller_helpers.rb +3 -1
- data/lib/devise/test/integration_helpers.rb +1 -1
- data/lib/devise/version.rb +1 -1
- data/lib/devise.rb +31 -12
- data/lib/generators/active_record/devise_generator.rb +17 -2
- data/lib/generators/devise/devise_generator.rb +1 -1
- data/lib/generators/devise/install_generator.rb +1 -5
- data/lib/generators/devise/views_generator.rb +1 -1
- data/lib/generators/templates/README +9 -1
- data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +1 -1
- data/lib/generators/templates/devise.rb +25 -11
- data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +1 -1
- metadata +21 -9
@@ -1,14 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DeviseHelper
|
4
|
-
# Retain this method for backwards compatibility, deprecated in
|
5
|
-
# devise/shared/error_messages partial
|
4
|
+
# Retain this method for backwards compatibility, deprecated in favor of modifying the
|
5
|
+
# devise/shared/error_messages partial.
|
6
6
|
def devise_error_messages!
|
7
7
|
ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
|
8
|
-
[Devise] `DeviseHelper
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
[Devise] `DeviseHelper#devise_error_messages!` is deprecated and will be
|
9
|
+
removed in the next major version.
|
10
|
+
|
11
|
+
Devise now uses a partial under "devise/shared/error_messages" to display
|
12
|
+
error messages by default, and make them easier to customize. Update your
|
13
|
+
views changing calls from:
|
14
|
+
|
15
|
+
<%= devise_error_messages! %>
|
16
|
+
|
17
|
+
to:
|
18
|
+
|
19
|
+
<%= render "devise/shared/error_messages", resource: resource %>
|
20
|
+
|
21
|
+
To start customizing how errors are displayed, you can copy the partial
|
22
|
+
from devise to your `app/views` folder. Alternatively, you can run
|
23
|
+
`rails g devise:views` which will copy all of them again to your app.
|
12
24
|
DEPRECATION
|
13
25
|
|
14
26
|
return "" if resource.errors.empty?
|
@@ -4,26 +4,26 @@ if defined?(ActionMailer)
|
|
4
4
|
class Devise::Mailer < Devise.parent_mailer.constantize
|
5
5
|
include Devise::Mailers::Helpers
|
6
6
|
|
7
|
-
def confirmation_instructions(record, token, opts={})
|
7
|
+
def confirmation_instructions(record, token, opts = {})
|
8
8
|
@token = token
|
9
9
|
devise_mail(record, :confirmation_instructions, opts)
|
10
10
|
end
|
11
11
|
|
12
|
-
def reset_password_instructions(record, token, opts={})
|
12
|
+
def reset_password_instructions(record, token, opts = {})
|
13
13
|
@token = token
|
14
14
|
devise_mail(record, :reset_password_instructions, opts)
|
15
15
|
end
|
16
16
|
|
17
|
-
def unlock_instructions(record, token, opts={})
|
17
|
+
def unlock_instructions(record, token, opts = {})
|
18
18
|
@token = token
|
19
19
|
devise_mail(record, :unlock_instructions, opts)
|
20
20
|
end
|
21
21
|
|
22
|
-
def email_changed(record, opts={})
|
22
|
+
def email_changed(record, opts = {})
|
23
23
|
devise_mail(record, :email_changed, opts)
|
24
24
|
end
|
25
25
|
|
26
|
-
def password_change(record, opts={})
|
26
|
+
def password_change(record, opts = {})
|
27
27
|
devise_mail(record, :password_change, opts)
|
28
28
|
end
|
29
29
|
end
|
@@ -38,6 +38,6 @@
|
|
38
38
|
|
39
39
|
<h3>Cancel my account</h3>
|
40
40
|
|
41
|
-
<
|
41
|
+
<div>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete %></div>
|
42
42
|
|
43
43
|
<%= link_to "Back", :back %>
|
@@ -20,6 +20,6 @@
|
|
20
20
|
|
21
21
|
<%- if devise_mapping.omniauthable? %>
|
22
22
|
<%- resource_class.omniauth_providers.each do |provider| %>
|
23
|
-
<%=
|
23
|
+
<%= button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false } %><br />
|
24
24
|
<% end %>
|
25
25
|
<% end %>
|
data/config/locales/en.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Additional translations at https://github.com/
|
1
|
+
# Additional translations at https://github.com/heartcombo/devise/wiki/I18n
|
2
2
|
|
3
3
|
en:
|
4
4
|
devise:
|
@@ -44,7 +44,7 @@ en:
|
|
44
44
|
signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
|
45
45
|
update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirmation link to confirm your new email address."
|
46
46
|
updated: "Your account has been updated successfully."
|
47
|
-
updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again"
|
47
|
+
updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again."
|
48
48
|
sessions:
|
49
49
|
signed_in: "Signed in successfully."
|
50
50
|
signed_out: "Signed out successfully."
|
@@ -36,14 +36,14 @@ module Devise
|
|
36
36
|
# before_action ->{ authenticate_blogger! :admin } # Redirects to the admin login page
|
37
37
|
# current_blogger :user # Preferably returns a User if one is signed in
|
38
38
|
#
|
39
|
-
def devise_group(group_name, opts={})
|
39
|
+
def devise_group(group_name, opts = {})
|
40
40
|
mappings = "[#{ opts[:contains].map { |m| ":#{m}" }.join(',') }]"
|
41
41
|
|
42
42
|
class_eval <<-METHODS, __FILE__, __LINE__ + 1
|
43
|
-
def authenticate_#{group_name}!(
|
43
|
+
def authenticate_#{group_name}!(favorite = nil, opts = {})
|
44
44
|
unless #{group_name}_signed_in?
|
45
45
|
mappings = #{mappings}
|
46
|
-
mappings.unshift mappings.delete(
|
46
|
+
mappings.unshift mappings.delete(favorite.to_sym) if favorite
|
47
47
|
mappings.each do |mapping|
|
48
48
|
opts[:scope] = mapping
|
49
49
|
warden.authenticate!(opts) if !devise_controller? || opts.delete(:force)
|
@@ -57,9 +57,9 @@ module Devise
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
def current_#{group_name}(
|
60
|
+
def current_#{group_name}(favorite = nil)
|
61
61
|
mappings = #{mappings}
|
62
|
-
mappings.unshift mappings.delete(
|
62
|
+
mappings.unshift mappings.delete(favorite.to_sym) if favorite
|
63
63
|
mappings.each do |mapping|
|
64
64
|
current = warden.authenticate(scope: mapping)
|
65
65
|
return current if current
|
@@ -113,7 +113,7 @@ module Devise
|
|
113
113
|
mapping = mapping.name
|
114
114
|
|
115
115
|
class_eval <<-METHODS, __FILE__, __LINE__ + 1
|
116
|
-
def authenticate_#{mapping}!(opts={})
|
116
|
+
def authenticate_#{mapping}!(opts = {})
|
117
117
|
opts[:scope] = :#{mapping}
|
118
118
|
warden.authenticate!(opts) if !devise_controller? || opts.delete(:force)
|
119
119
|
end
|
@@ -252,7 +252,7 @@ module Devise
|
|
252
252
|
# Overwrite Rails' handle unverified request to sign out all scopes,
|
253
253
|
# clear run strategies and remove cached variables.
|
254
254
|
def handle_unverified_request
|
255
|
-
super # call the default
|
255
|
+
super # call the default behavior which resets/nullifies/raises
|
256
256
|
request.env["devise.skip_storage"] = true
|
257
257
|
sign_out_all_scopes(false)
|
258
258
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Controllers
|
5
|
+
# Custom Responder to configure default statuses that only apply to Devise,
|
6
|
+
# and allow to integrate more easily with Hotwire/Turbo.
|
7
|
+
class Responder < ActionController::Responder
|
8
|
+
if respond_to?(:error_status=) && respond_to?(:redirect_status=)
|
9
|
+
self.error_status = :ok
|
10
|
+
self.redirect_status = :found
|
11
|
+
else
|
12
|
+
# TODO: remove this support for older Rails versions, which aren't supported by Turbo
|
13
|
+
# and/or responders. It won't allow configuring a custom response, but it allows Devise
|
14
|
+
# to use these methods and defaults across the implementation more easily.
|
15
|
+
def self.error_status
|
16
|
+
:ok
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.redirect_status
|
20
|
+
:found
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.error_status=(*)
|
24
|
+
warn "[DEVISE] Setting the error status on the Devise responder has no effect with this " \
|
25
|
+
"version of `responders`, please make sure you're using a newer version. Check the changelog for more info."
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.redirect_status=(*)
|
29
|
+
warn "[DEVISE] Setting the redirect status on the Devise responder has no effect with this " \
|
30
|
+
"version of `responders`, please make sure you're using a newer version. Check the changelog for more info."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -10,7 +10,7 @@ module Devise
|
|
10
10
|
# cause exceptions to be thrown from this method; if you simply want to check
|
11
11
|
# if a scope has already previously been authenticated without running
|
12
12
|
# authentication hooks, you can directly call `warden.authenticated?(scope: scope)`
|
13
|
-
def signed_in?(scope=nil)
|
13
|
+
def signed_in?(scope = nil)
|
14
14
|
[scope || Devise.mappings.keys].flatten.any? do |_scope|
|
15
15
|
warden.authenticate?(scope: _scope)
|
16
16
|
end
|
@@ -21,7 +21,7 @@ module Devise
|
|
21
21
|
# to the set_user method in warden.
|
22
22
|
# If you are using a custom warden strategy and the timeoutable module, you have to
|
23
23
|
# set `env["devise.skip_timeout"] = true` in the request to use this method, like we do
|
24
|
-
# in the sessions controller: https://github.com/
|
24
|
+
# in the sessions controller: https://github.com/heartcombo/devise/blob/main/app/controllers/devise/sessions_controller.rb#L7
|
25
25
|
#
|
26
26
|
# Examples:
|
27
27
|
#
|
@@ -77,7 +77,7 @@ module Devise
|
|
77
77
|
# sign_out :user # sign_out(scope)
|
78
78
|
# sign_out @user # sign_out(resource)
|
79
79
|
#
|
80
|
-
def sign_out(resource_or_scope=nil)
|
80
|
+
def sign_out(resource_or_scope = nil)
|
81
81
|
return sign_out_all_scopes unless resource_or_scope
|
82
82
|
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
83
83
|
user = warden.user(scope: scope, run_callbacks: false) # If there is no user
|
@@ -92,7 +92,7 @@ module Devise
|
|
92
92
|
# Sign out all active users or scopes. This helper is useful for signing out all roles
|
93
93
|
# in one click. This signs out ALL scopes in warden. Returns true if there was at least one logout
|
94
94
|
# and false if there was no user logged in on all scopes.
|
95
|
-
def sign_out_all_scopes(lock=true)
|
95
|
+
def sign_out_all_scopes(lock = true)
|
96
96
|
users = Devise.mappings.keys.map { |s| warden.user(scope: s, run_callbacks: false) }
|
97
97
|
|
98
98
|
warden.logout
|
@@ -106,10 +106,12 @@ module Devise
|
|
106
106
|
private
|
107
107
|
|
108
108
|
def expire_data_after_sign_in!
|
109
|
+
# TODO: remove once Rails 5.2+ and forward are only supported.
|
109
110
|
# session.keys will return an empty array if the session is not yet loaded.
|
110
111
|
# This is a bug in both Rack and Rails.
|
111
112
|
# A call to #empty? forces the session to be loaded.
|
112
113
|
session.empty?
|
114
|
+
|
113
115
|
session.keys.grep(/^devise\./).each { |k| session.delete(k) }
|
114
116
|
end
|
115
117
|
|
data/lib/devise/failure_app.rb
CHANGED
@@ -71,8 +71,11 @@ module Devise
|
|
71
71
|
end
|
72
72
|
|
73
73
|
flash.now[:alert] = i18n_message(:invalid) if is_flashing_format?
|
74
|
-
|
75
|
-
|
74
|
+
self.response = recall_app(warden_options[:recall]).call(request.env).tap { |response|
|
75
|
+
response[0] = Rack::Utils.status_code(
|
76
|
+
response[0].in?(300..399) ? Devise.responder.redirect_status : Devise.responder.error_status
|
77
|
+
)
|
78
|
+
}
|
76
79
|
end
|
77
80
|
|
78
81
|
def redirect
|
@@ -107,7 +110,7 @@ module Devise
|
|
107
110
|
options[:authentication_keys] = keys.join(I18n.translate(:"support.array.words_connector"))
|
108
111
|
options = i18n_options(options)
|
109
112
|
|
110
|
-
I18n.t(:"#{scope}.#{message}", options)
|
113
|
+
I18n.t(:"#{scope}.#{message}", **options)
|
111
114
|
else
|
112
115
|
message.to_s
|
113
116
|
end
|
@@ -152,7 +155,7 @@ module Devise
|
|
152
155
|
|
153
156
|
# We need to add the rootpath to `script_name` manually for applications that use a Rails
|
154
157
|
# version lower than 5.1. Otherwise, it is going to generate a wrong path for Engines
|
155
|
-
# that use Devise. Remove it when the support of Rails 5.0 is
|
158
|
+
# that use Devise. Remove it when the support of Rails 5.0 is dropped.
|
156
159
|
elsif root_path_defined?(context) && !rails_51_and_up?
|
157
160
|
rootpath = context.routes.url_helpers.root_path
|
158
161
|
opts[:script_name] = rootpath.chomp('/') if rootpath.length > 1
|
@@ -168,7 +171,7 @@ module Devise
|
|
168
171
|
end
|
169
172
|
|
170
173
|
def skip_format?
|
171
|
-
%w(html */*).include? request_format.to_s
|
174
|
+
%w(html */* turbo_stream).include? request_format.to_s
|
172
175
|
end
|
173
176
|
|
174
177
|
# Choose whether we should respond in an HTTP authentication fashion,
|
@@ -4,6 +4,11 @@ Warden::Manager.after_authentication do |record, warden, options|
|
|
4
4
|
clean_up_for_winning_strategy = !warden.winning_strategy.respond_to?(:clean_up_csrf?) ||
|
5
5
|
warden.winning_strategy.clean_up_csrf?
|
6
6
|
if Devise.clean_up_csrf_token_on_authentication && clean_up_for_winning_strategy
|
7
|
-
warden.request.
|
7
|
+
if warden.request.respond_to?(:reset_csrf_token)
|
8
|
+
# Rails 7.1+
|
9
|
+
warden.request.reset_csrf_token
|
10
|
+
else
|
11
|
+
warden.request.session.try(:delete, :_csrf_token)
|
12
|
+
end
|
8
13
|
end
|
9
14
|
end
|
@@ -3,10 +3,7 @@
|
|
3
3
|
# After each sign in, if resource responds to failed_attempts, sets it to 0
|
4
4
|
# This is only triggered when the user is explicitly set (with set_user)
|
5
5
|
Warden::Manager.after_set_user except: :fetch do |record, warden, options|
|
6
|
-
if record.respond_to?(:
|
7
|
-
|
8
|
-
record.failed_attempts = 0
|
9
|
-
record.save(validate: false)
|
10
|
-
end
|
6
|
+
if record.respond_to?(:reset_failed_attempts!) && warden.authenticated?(options[:scope])
|
7
|
+
record.reset_failed_attempts!
|
11
8
|
end
|
12
9
|
end
|
@@ -21,8 +21,8 @@ Warden::Manager.after_set_user do |record, warden, options|
|
|
21
21
|
|
22
22
|
proxy = Devise::Hooks::Proxy.new(warden)
|
23
23
|
|
24
|
-
if
|
25
|
-
|
24
|
+
if !env['devise.skip_timeout'] &&
|
25
|
+
record.timedout?(last_request_at) &&
|
26
26
|
!proxy.remember_me_is_active?(record)
|
27
27
|
Devise.sign_out_all_scopes ? proxy.sign_out : proxy.sign_out(scope)
|
28
28
|
throw :warden, scope: scope, message: :timeout
|
data/lib/devise/mapping.rb
CHANGED
@@ -46,7 +46,7 @@ module Devise
|
|
46
46
|
raise "Could not find a valid mapping for #{obj.inspect}"
|
47
47
|
end
|
48
48
|
|
49
|
-
def self.find_by_path!(path, path_type
|
49
|
+
def self.find_by_path!(path, path_type = :fullpath)
|
50
50
|
Devise.mappings.each_value { |m| return m if path.include?(m.send(path_type)) }
|
51
51
|
raise "Could not find a valid mapping for path #{path.inspect}"
|
52
52
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'devise/hooks/activatable'
|
4
4
|
require 'devise/hooks/csrf_cleaner'
|
5
|
+
require 'devise/rails/deprecated_constant_accessor'
|
5
6
|
|
6
7
|
module Devise
|
7
8
|
module Models
|
@@ -9,7 +10,7 @@ module Devise
|
|
9
10
|
#
|
10
11
|
# == Options
|
11
12
|
#
|
12
|
-
# Authenticatable adds the following options to
|
13
|
+
# Authenticatable adds the following options to +devise+:
|
13
14
|
#
|
14
15
|
# * +authentication_keys+: parameters used for authentication. By default [:email].
|
15
16
|
#
|
@@ -55,11 +56,14 @@ module Devise
|
|
55
56
|
module Authenticatable
|
56
57
|
extend ActiveSupport::Concern
|
57
58
|
|
58
|
-
|
59
|
+
UNSAFE_ATTRIBUTES_FOR_SERIALIZATION = [:encrypted_password, :reset_password_token, :reset_password_sent_at,
|
59
60
|
:remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip,
|
60
61
|
:last_sign_in_ip, :password_salt, :confirmation_token, :confirmed_at, :confirmation_sent_at,
|
61
62
|
:remember_token, :unconfirmed_email, :failed_attempts, :unlock_token, :locked_at]
|
62
63
|
|
64
|
+
include Devise::DeprecatedConstantAccessor
|
65
|
+
deprecate_constant "BLACKLIST_FOR_SERIALIZATION", "Devise::Models::Authenticatable::UNSAFE_ATTRIBUTES_FOR_SERIALIZATION"
|
66
|
+
|
63
67
|
included do
|
64
68
|
class_attribute :devise_modules, instance_writer: false
|
65
69
|
self.devise_modules ||= []
|
@@ -104,12 +108,12 @@ module Devise
|
|
104
108
|
# given to :except will simply add names to exempt to Devise internal list.
|
105
109
|
def serializable_hash(options = nil)
|
106
110
|
options = options.try(:dup) || {}
|
107
|
-
options[:except] = Array(options[:except])
|
111
|
+
options[:except] = Array(options[:except]).dup
|
108
112
|
|
109
113
|
if options[:force_except]
|
110
114
|
options[:except].concat Array(options[:force_except])
|
111
115
|
else
|
112
|
-
options[:except].concat
|
116
|
+
options[:except].concat UNSAFE_ATTRIBUTES_FOR_SERIALIZATION
|
113
117
|
end
|
114
118
|
|
115
119
|
super(options)
|
@@ -152,7 +156,8 @@ module Devise
|
|
152
156
|
# # If the record is new or changed then delay the
|
153
157
|
# # delivery until the after_commit callback otherwise
|
154
158
|
# # send now because after_commit will not be called.
|
155
|
-
#
|
159
|
+
# # For Rails < 6 use `changed?` instead of `saved_changes?`.
|
160
|
+
# if new_record? || saved_changes?
|
156
161
|
# pending_devise_notifications << [notification, args]
|
157
162
|
# else
|
158
163
|
# render_and_send_devise_message(notification, *args)
|
@@ -271,17 +276,17 @@ module Devise
|
|
271
276
|
find_first_by_auth_conditions(tainted_conditions)
|
272
277
|
end
|
273
278
|
|
274
|
-
def find_first_by_auth_conditions(tainted_conditions, opts={})
|
279
|
+
def find_first_by_auth_conditions(tainted_conditions, opts = {})
|
275
280
|
to_adapter.find_first(devise_parameter_filter.filter(tainted_conditions).merge(opts))
|
276
281
|
end
|
277
282
|
|
278
283
|
# Find or initialize a record setting an error if it can't be found.
|
279
|
-
def find_or_initialize_with_error_by(attribute, value, error
|
284
|
+
def find_or_initialize_with_error_by(attribute, value, error = :invalid) #:nodoc:
|
280
285
|
find_or_initialize_with_errors([attribute], { attribute => value }, error)
|
281
286
|
end
|
282
287
|
|
283
288
|
# Find or initialize a record with group of attributes based on a list of required attributes.
|
284
|
-
def find_or_initialize_with_errors(required_attributes, attributes, error
|
289
|
+
def find_or_initialize_with_errors(required_attributes, attributes, error = :invalid) #:nodoc:
|
285
290
|
attributes.try(:permit!)
|
286
291
|
attributes = attributes.to_h.with_indifferent_access
|
287
292
|
.slice(*required_attributes)
|
@@ -48,7 +48,7 @@ module Devise
|
|
48
48
|
included do
|
49
49
|
before_create :generate_confirmation_token, if: :confirmation_required?
|
50
50
|
after_create :skip_reconfirmation_in_callback!, if: :send_confirmation_notification?
|
51
|
-
if
|
51
|
+
if Devise::Orm.active_record?(self) # ActiveRecord
|
52
52
|
after_commit :send_on_create_confirmation_instructions, on: :create, if: :send_confirmation_notification?
|
53
53
|
after_commit :send_reconfirmation_instructions, on: :update, if: :reconfirmation_required?
|
54
54
|
else # Mongoid
|
@@ -76,7 +76,7 @@ module Devise
|
|
76
76
|
# Confirm a user by setting it's confirmed_at to actual time. If the user
|
77
77
|
# is already confirmed, add an error to email field. If the user is invalid
|
78
78
|
# add errors
|
79
|
-
def confirm(args={})
|
79
|
+
def confirm(args = {})
|
80
80
|
pending_any_confirmation do
|
81
81
|
if confirmation_period_expired?
|
82
82
|
self.errors.add(:email, :confirmation_period_expired,
|
@@ -258,44 +258,23 @@ module Devise
|
|
258
258
|
generate_confirmation_token && save(validate: false)
|
259
259
|
end
|
260
260
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
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
|
261
|
+
|
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.devise_email_in_database
|
266
|
+
self.confirmation_token = nil
|
267
|
+
generate_confirmation_token
|
277
268
|
end
|
278
269
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
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
|
270
|
+
def postpone_email_change?
|
271
|
+
postpone = self.class.reconfirmable &&
|
272
|
+
devise_will_save_change_to_email? &&
|
273
|
+
!@bypass_confirmation_postpone &&
|
274
|
+
self.email.present? &&
|
275
|
+
(!@skip_reconfirmation_in_callback || !self.devise_email_in_database.nil?)
|
276
|
+
@bypass_confirmation_postpone = false
|
277
|
+
postpone
|
299
278
|
end
|
300
279
|
|
301
280
|
def reconfirmation_required?
|
@@ -334,7 +313,7 @@ module Devise
|
|
334
313
|
# confirmation instructions to it. If not, try searching for a user by unconfirmed_email
|
335
314
|
# field. If no user is found, returns a new user with an email not found error.
|
336
315
|
# Options must contain the user email
|
337
|
-
def send_confirmation_instructions(attributes={})
|
316
|
+
def send_confirmation_instructions(attributes = {})
|
338
317
|
confirmable = find_by_unconfirmed_email_with_errors(attributes) if reconfirmable
|
339
318
|
unless confirmable.try(:persisted?)
|
340
319
|
confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
|
@@ -7,9 +7,13 @@ module Devise
|
|
7
7
|
# Authenticatable Module, responsible for hashing the password and
|
8
8
|
# validating the authenticity of a user while signing in.
|
9
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
|
+
#
|
10
14
|
# == Options
|
11
15
|
#
|
12
|
-
# DatabaseAuthenticatable adds the following options to
|
16
|
+
# DatabaseAuthenticatable adds the following options to +devise+:
|
13
17
|
#
|
14
18
|
# * +pepper+: a random string used to provide a more secure hash. Use
|
15
19
|
# `rails secret` to generate new keys.
|
@@ -38,7 +42,7 @@ module Devise
|
|
38
42
|
def initialize(*args, &block)
|
39
43
|
@skip_email_changed_notification = false
|
40
44
|
@skip_password_change_notification = false
|
41
|
-
super
|
45
|
+
super
|
42
46
|
end
|
43
47
|
|
44
48
|
# Skips sending the email changed notification after_update
|
@@ -173,16 +177,9 @@ module Devise
|
|
173
177
|
encrypted_password[0,29] if encrypted_password
|
174
178
|
end
|
175
179
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
send_devise_notification(:email_changed, to: email_before_last_save)
|
180
|
-
end
|
181
|
-
else
|
182
|
-
# Send notification to user when email changes.
|
183
|
-
def send_email_changed_notification
|
184
|
-
send_devise_notification(:email_changed, to: email_was)
|
185
|
-
end
|
180
|
+
# Send notification to user when email changes.
|
181
|
+
def send_email_changed_notification
|
182
|
+
send_devise_notification(:email_changed, to: devise_email_before_last_save)
|
186
183
|
end
|
187
184
|
|
188
185
|
# Send notification to user when password changes.
|
@@ -195,30 +192,18 @@ module Devise
|
|
195
192
|
# Hashes the password using bcrypt. Custom hash functions should override
|
196
193
|
# this method to apply their own algorithm.
|
197
194
|
#
|
198
|
-
# See https://github.com/
|
195
|
+
# See https://github.com/heartcombo/devise-encryptable for examples
|
199
196
|
# of other hashing engines.
|
200
197
|
def password_digest(password)
|
201
198
|
Devise::Encryptor.digest(self.class, password)
|
202
199
|
end
|
203
200
|
|
204
|
-
|
205
|
-
|
206
|
-
self.class.send_email_changed_notification && saved_change_to_email? && !@skip_email_changed_notification
|
207
|
-
end
|
208
|
-
else
|
209
|
-
def send_email_changed_notification?
|
210
|
-
self.class.send_email_changed_notification && email_changed? && !@skip_email_changed_notification
|
211
|
-
end
|
201
|
+
def send_email_changed_notification?
|
202
|
+
self.class.send_email_changed_notification && devise_saved_change_to_email? && !@skip_email_changed_notification
|
212
203
|
end
|
213
204
|
|
214
|
-
|
215
|
-
|
216
|
-
self.class.send_password_change_notification && saved_change_to_encrypted_password? && !@skip_password_change_notification
|
217
|
-
end
|
218
|
-
else
|
219
|
-
def send_password_change_notification?
|
220
|
-
self.class.send_password_change_notification && encrypted_password_changed? && !@skip_password_change_notification
|
221
|
-
end
|
205
|
+
def send_password_change_notification?
|
206
|
+
self.class.send_password_change_notification && devise_saved_change_to_encrypted_password? && !@skip_password_change_notification
|
222
207
|
end
|
223
208
|
|
224
209
|
module ClassMethods
|
@@ -18,7 +18,7 @@ module Devise
|
|
18
18
|
# * +maximum_attempts+: how many attempts should be accepted before blocking the user.
|
19
19
|
# * +lock_strategy+: lock the user account by :failed_attempts or :none.
|
20
20
|
# * +unlock_strategy+: unlock the user account by :time, :email, :both or :none.
|
21
|
-
# * +unlock_in+: the time you want to
|
21
|
+
# * +unlock_in+: the time you want to unlock the user after lock happens. Only available when unlock_strategy is :time or :both.
|
22
22
|
# * +unlock_keys+: the keys you want to use when locking and unlocking an account
|
23
23
|
#
|
24
24
|
module Lockable
|
@@ -57,6 +57,14 @@ module Devise
|
|
57
57
|
save(validate: false)
|
58
58
|
end
|
59
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
|
+
|
60
68
|
# Verifies whether a user is locked or not.
|
61
69
|
def access_locked?
|
62
70
|
!!locked_at && !lock_expired?
|
@@ -110,7 +118,7 @@ module Devise
|
|
110
118
|
false
|
111
119
|
end
|
112
120
|
end
|
113
|
-
|
121
|
+
|
114
122
|
def increment_failed_attempts
|
115
123
|
self.class.increment_counter(:failed_attempts, id)
|
116
124
|
reload
|
@@ -168,7 +176,7 @@ module Devise
|
|
168
176
|
# unlock instructions to it. If not user is found, returns a new user
|
169
177
|
# with an email not found error.
|
170
178
|
# Options must contain the user's unlock keys
|
171
|
-
def send_unlock_instructions(attributes={})
|
179
|
+
def send_unlock_instructions(attributes = {})
|
172
180
|
lockable = find_or_initialize_with_errors(unlock_keys, attributes, :not_found)
|
173
181
|
lockable.resend_unlock_instructions if lockable.persisted?
|
174
182
|
lockable
|
@@ -8,11 +8,11 @@ module Devise
|
|
8
8
|
#
|
9
9
|
# == Options
|
10
10
|
#
|
11
|
-
# Oauthable adds the following options to
|
11
|
+
# Oauthable adds the following options to +devise+:
|
12
12
|
#
|
13
13
|
# * +omniauth_providers+: Which providers are available to this model. It expects an array:
|
14
14
|
#
|
15
|
-
#
|
15
|
+
# devise :database_authenticatable, :omniauthable, omniauth_providers: [:twitter]
|
16
16
|
#
|
17
17
|
module Omniauthable
|
18
18
|
extend ActiveSupport::Concern
|