devise 2.1.2 → 3.5.10
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of devise might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/.travis.yml +39 -10
- data/.yardopts +9 -0
- data/{CHANGELOG.rdoc → CHANGELOG.md} +445 -112
- data/CODE_OF_CONDUCT.md +22 -0
- data/CONTRIBUTING.md +16 -0
- data/Gemfile +10 -15
- data/Gemfile.lock +151 -129
- data/MIT-LICENSE +1 -1
- data/README.md +256 -96
- data/Rakefile +4 -2
- data/app/controllers/devise/confirmations_controller.rb +15 -7
- data/app/controllers/devise/omniauth_callbacks_controller.rb +6 -2
- data/app/controllers/devise/passwords_controller.rb +33 -9
- data/app/controllers/devise/registrations_controller.rb +66 -26
- data/app/controllers/devise/sessions_controller.rb +52 -21
- data/app/controllers/devise/unlocks_controller.rb +11 -6
- data/app/controllers/devise_controller.rb +65 -58
- data/app/helpers/devise_helper.rb +2 -2
- data/app/mailers/devise/mailer.rb +19 -10
- data/app/views/devise/confirmations/new.html.erb +8 -4
- data/app/views/devise/mailer/confirmation_instructions.html.erb +2 -2
- data/app/views/devise/mailer/password_change.html.erb +3 -0
- data/app/views/devise/mailer/reset_password_instructions.html.erb +2 -2
- data/app/views/devise/mailer/unlock_instructions.html.erb +2 -2
- data/app/views/devise/passwords/edit.html.erb +15 -6
- data/app/views/devise/passwords/new.html.erb +8 -4
- data/app/views/devise/registrations/edit.html.erb +29 -15
- data/app/views/devise/registrations/new.html.erb +19 -8
- data/app/views/devise/sessions/new.html.erb +17 -8
- data/app/views/devise/shared/{_links.erb → _links.html.erb} +4 -4
- data/app/views/devise/unlocks/new.html.erb +8 -4
- data/config/locales/en.yml +51 -47
- data/devise.gemspec +8 -6
- data/devise.png +0 -0
- data/gemfiles/Gemfile.rails-3.2-stable +29 -0
- data/gemfiles/Gemfile.rails-3.2-stable.lock +172 -0
- data/gemfiles/Gemfile.rails-4.0-stable +30 -0
- data/gemfiles/Gemfile.rails-4.0-stable.lock +166 -0
- data/gemfiles/Gemfile.rails-4.1-stable +30 -0
- data/gemfiles/Gemfile.rails-4.1-stable.lock +171 -0
- data/gemfiles/Gemfile.rails-4.2-stable +30 -0
- data/gemfiles/Gemfile.rails-4.2-stable.lock +193 -0
- data/lib/devise/controllers/helpers.rb +126 -108
- data/lib/devise/controllers/rememberable.rb +19 -17
- data/lib/devise/controllers/scoped_views.rb +1 -1
- data/lib/devise/controllers/sign_in_out.rb +96 -0
- data/lib/devise/controllers/store_location.rb +58 -0
- data/lib/devise/controllers/url_helpers.rb +7 -7
- data/lib/devise/encryptor.rb +22 -0
- data/lib/devise/failure_app.rb +85 -25
- data/lib/devise/hooks/activatable.rb +5 -6
- data/lib/devise/hooks/csrf_cleaner.rb +7 -0
- data/lib/devise/hooks/forgetable.rb +1 -1
- data/lib/devise/hooks/lockable.rb +2 -2
- data/lib/devise/hooks/proxy.rb +21 -0
- data/lib/devise/hooks/rememberable.rb +5 -4
- data/lib/devise/hooks/timeoutable.rb +16 -8
- data/lib/devise/hooks/trackable.rb +1 -1
- data/lib/devise/mailers/helpers.rb +27 -23
- data/lib/devise/mapping.rb +11 -7
- data/lib/devise/models/authenticatable.rb +82 -66
- data/lib/devise/models/confirmable.rb +142 -55
- data/lib/devise/models/database_authenticatable.rb +59 -15
- data/lib/devise/models/lockable.rb +41 -30
- data/lib/devise/models/omniauthable.rb +3 -3
- data/lib/devise/models/recoverable.rb +56 -41
- data/lib/devise/models/rememberable.rb +65 -27
- data/lib/devise/models/timeoutable.rb +2 -8
- data/lib/devise/models/trackable.rb +6 -4
- data/lib/devise/models/validatable.rb +9 -9
- data/lib/devise/models.rb +4 -13
- data/lib/devise/modules.rb +10 -11
- data/lib/devise/omniauth/url_helpers.rb +2 -2
- data/lib/devise/orm/active_record.rb +1 -1
- data/lib/devise/orm/mongoid.rb +1 -1
- data/lib/devise/{param_filter.rb → parameter_filter.rb} +10 -11
- data/lib/devise/parameter_sanitizer.rb +99 -0
- data/lib/devise/rails/routes.rb +173 -115
- data/lib/devise/rails/warden_compat.rb +10 -31
- data/lib/devise/rails.rb +14 -12
- data/lib/devise/strategies/authenticatable.rb +26 -26
- data/lib/devise/strategies/base.rb +1 -1
- data/lib/devise/strategies/database_authenticatable.rb +8 -4
- data/lib/devise/strategies/rememberable.rb +15 -5
- data/lib/devise/test_helpers.rb +7 -5
- data/lib/devise/time_inflector.rb +14 -0
- data/lib/devise/token_generator.rb +70 -0
- data/lib/devise/version.rb +1 -1
- data/lib/devise.rb +110 -52
- data/lib/generators/active_record/devise_generator.rb +34 -18
- data/lib/generators/active_record/templates/migration.rb +5 -6
- data/lib/generators/active_record/templates/migration_existing.rb +5 -6
- data/lib/generators/devise/controllers_generator.rb +44 -0
- data/lib/generators/devise/devise_generator.rb +5 -3
- data/lib/generators/devise/install_generator.rb +5 -0
- data/lib/generators/devise/orm_helpers.rb +25 -6
- data/lib/generators/devise/views_generator.rb +52 -22
- data/lib/generators/mongoid/devise_generator.rb +21 -26
- data/lib/generators/templates/README +9 -5
- data/lib/generators/templates/controllers/README +14 -0
- data/lib/generators/templates/controllers/confirmations_controller.rb +28 -0
- data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +28 -0
- data/lib/generators/templates/controllers/passwords_controller.rb +32 -0
- data/lib/generators/templates/controllers/registrations_controller.rb +60 -0
- data/lib/generators/templates/controllers/sessions_controller.rb +25 -0
- data/lib/generators/templates/controllers/unlocks_controller.rb +28 -0
- data/lib/generators/templates/devise.rb +80 -43
- data/lib/generators/templates/markerb/confirmation_instructions.markerb +2 -2
- data/lib/generators/templates/markerb/password_change.markerb +3 -0
- data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
- data/lib/generators/templates/markerb/unlock_instructions.markerb +2 -2
- data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +3 -2
- data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +4 -4
- data/lib/generators/templates/simple_form_for/passwords/new.html.erb +2 -2
- data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +11 -6
- data/lib/generators/templates/simple_form_for/registrations/new.html.erb +4 -4
- data/lib/generators/templates/simple_form_for/sessions/new.html.erb +6 -6
- data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +3 -2
- data/script/cached-bundle +49 -0
- data/script/s3-put +71 -0
- data/test/controllers/custom_registrations_controller_test.rb +40 -0
- data/test/controllers/helper_methods_test.rb +21 -0
- data/test/controllers/helpers_test.rb +95 -32
- data/test/controllers/inherited_controller_i18n_messages_test.rb +51 -0
- data/test/controllers/internal_helpers_test.rb +39 -14
- data/test/controllers/load_hooks_controller_test.rb +19 -0
- data/test/controllers/passwords_controller_test.rb +31 -0
- data/test/controllers/sessions_controller_test.rb +66 -6
- data/test/controllers/url_helpers_test.rb +10 -4
- data/test/delegator_test.rb +1 -1
- data/test/devise_test.rb +45 -10
- data/test/failure_app_test.rb +121 -27
- data/test/generators/active_record_generator_test.rb +48 -8
- data/test/generators/controllers_generator_test.rb +48 -0
- data/test/generators/devise_generator_test.rb +2 -2
- data/test/generators/mongoid_generator_test.rb +3 -3
- data/test/generators/views_generator_test.rb +54 -3
- data/test/helpers/devise_helper_test.rb +18 -20
- data/test/integration/authenticatable_test.rb +161 -65
- data/test/integration/confirmable_test.rb +146 -77
- data/test/integration/database_authenticatable_test.rb +43 -30
- data/test/integration/http_authenticatable_test.rb +30 -22
- data/test/integration/lockable_test.rb +64 -49
- data/test/integration/omniauthable_test.rb +17 -15
- data/test/integration/recoverable_test.rb +111 -70
- data/test/integration/registerable_test.rb +114 -79
- data/test/integration/rememberable_test.rb +87 -31
- data/test/integration/timeoutable_test.rb +77 -33
- data/test/integration/trackable_test.rb +5 -5
- data/test/mailers/confirmation_instructions_test.rb +28 -8
- data/test/mailers/reset_password_instructions_test.rb +21 -8
- data/test/mailers/unlock_instructions_test.rb +20 -6
- data/test/mapping_test.rb +12 -5
- data/test/models/authenticatable_test.rb +17 -1
- data/test/models/confirmable_test.rb +216 -62
- data/test/models/database_authenticatable_test.rb +129 -49
- data/test/models/lockable_test.rb +132 -45
- data/test/models/recoverable_test.rb +100 -54
- data/test/models/rememberable_test.rb +89 -94
- data/test/models/serializable_test.rb +12 -11
- data/test/models/timeoutable_test.rb +6 -1
- data/test/models/trackable_test.rb +28 -0
- data/test/models/validatable_test.rb +31 -21
- data/test/models_test.rb +22 -48
- data/test/omniauth/config_test.rb +4 -4
- data/test/omniauth/url_helpers_test.rb +7 -4
- data/test/orm/active_record.rb +1 -0
- data/test/orm/mongoid.rb +2 -3
- data/test/parameter_sanitizer_test.rb +81 -0
- data/test/rails_app/Rakefile +0 -4
- data/test/rails_app/app/active_record/shim.rb +1 -1
- data/test/rails_app/app/active_record/user_on_engine.rb +7 -0
- data/test/rails_app/app/active_record/user_on_main_app.rb +7 -0
- data/test/rails_app/app/active_record/user_without_email.rb +8 -0
- data/test/rails_app/app/controllers/admins/sessions_controller.rb +1 -1
- data/test/rails_app/app/controllers/admins_controller.rb +0 -5
- data/test/rails_app/app/controllers/application_controller.rb +6 -2
- data/test/rails_app/app/controllers/application_with_fake_engine.rb +30 -0
- data/test/rails_app/app/controllers/custom/registrations_controller.rb +31 -0
- data/test/rails_app/app/controllers/home_controller.rb +1 -1
- data/test/rails_app/app/controllers/publisher/registrations_controller.rb +1 -1
- data/test/rails_app/app/controllers/publisher/sessions_controller.rb +1 -1
- data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +4 -4
- data/test/rails_app/app/controllers/users_controller.rb +12 -4
- data/test/rails_app/app/mailers/users/from_proc_mailer.rb +3 -0
- data/test/rails_app/app/mailers/users/mailer.rb +1 -1
- data/test/rails_app/app/mailers/users/reply_to_mailer.rb +4 -0
- data/test/rails_app/app/mongoid/admin.rb +12 -10
- data/test/rails_app/app/mongoid/shim.rb +4 -5
- data/test/rails_app/app/mongoid/user.rb +19 -22
- data/test/rails_app/app/mongoid/user_on_engine.rb +39 -0
- data/test/rails_app/app/mongoid/user_on_main_app.rb +39 -0
- data/test/rails_app/app/mongoid/user_without_email.rb +33 -0
- data/test/rails_app/app/views/admins/sessions/new.html.erb +1 -1
- data/test/rails_app/app/views/home/admin_dashboard.html.erb +1 -1
- data/test/rails_app/app/views/home/index.html.erb +1 -1
- data/test/rails_app/app/views/home/join.html.erb +1 -1
- data/test/rails_app/app/views/home/user_dashboard.html.erb +1 -1
- data/test/rails_app/app/views/layouts/application.html.erb +1 -1
- data/test/rails_app/app/views/users/edit_form.html.erb +1 -0
- data/test/rails_app/bin/bundle +3 -0
- data/test/rails_app/bin/rails +4 -0
- data/test/rails_app/bin/rake +4 -0
- data/test/rails_app/config/application.rb +4 -5
- data/test/rails_app/config/boot.rb +9 -3
- data/test/rails_app/config/environment.rb +2 -2
- data/test/rails_app/config/environments/development.rb +19 -7
- data/test/rails_app/config/environments/production.rb +68 -17
- data/test/rails_app/config/environments/test.rb +24 -16
- data/test/rails_app/config/initializers/devise.rb +22 -20
- data/test/rails_app/config/initializers/secret_token.rb +8 -2
- data/test/rails_app/config/initializers/session_store.rb +1 -0
- data/test/rails_app/config/routes.rb +71 -46
- data/test/rails_app/db/migrate/20100401102949_create_tables.rb +9 -12
- data/test/rails_app/db/schema.rb +21 -18
- data/test/rails_app/lib/shared_admin.rb +7 -4
- data/test/rails_app/lib/shared_user.rb +6 -3
- data/test/rails_app/lib/shared_user_without_email.rb +26 -0
- data/test/rails_app/lib/shared_user_without_omniauth.rb +13 -0
- data/test/rails_test.rb +9 -0
- data/test/routes_test.rb +94 -78
- data/test/support/action_controller/record_identifier.rb +10 -0
- data/test/support/assertions.rb +2 -3
- data/test/support/helpers.rb +18 -32
- data/test/support/integration.rb +17 -16
- data/test/support/locale/en.yml +4 -0
- data/test/support/mongoid.yml +6 -0
- data/test/test_helper.rb +8 -1
- data/test/test_helpers_test.rb +64 -20
- data/test/test_models.rb +33 -0
- data/test/time_helpers.rb +137 -0
- metadata +172 -51
- data/app/views/devise/_links.erb +0 -3
- data/gemfiles/Gemfile.rails-3.1.x +0 -35
- data/gemfiles/Gemfile.rails-3.1.x.lock +0 -167
- data/lib/devise/models/token_authenticatable.rb +0 -77
- data/lib/devise/strategies/token_authenticatable.rb +0 -56
- data/test/indifferent_hash.rb +0 -33
- data/test/integration/token_authenticatable_test.rb +0 -161
- data/test/models/token_authenticatable_test.rb +0 -55
- data/test/rails_app/script/rails +0 -10
data/lib/devise/mapping.rb
CHANGED
@@ -23,23 +23,25 @@ module Devise
|
|
23
23
|
#
|
24
24
|
class Mapping #:nodoc:
|
25
25
|
attr_reader :singular, :scoped_path, :path, :controllers, :path_names,
|
26
|
-
:class_name, :sign_out_via, :format, :used_routes, :used_helpers,
|
26
|
+
:class_name, :sign_out_via, :format, :used_routes, :used_helpers,
|
27
|
+
:failure_app, :router_name
|
27
28
|
|
28
29
|
alias :name :singular
|
29
30
|
|
30
31
|
# Receives an object and find a scope for it. If a scope cannot be found,
|
31
32
|
# raises an error. If a symbol is given, it's considered to be the scope.
|
32
|
-
def self.find_scope!(
|
33
|
-
|
33
|
+
def self.find_scope!(obj)
|
34
|
+
obj = obj.devise_scope if obj.respond_to?(:devise_scope)
|
35
|
+
case obj
|
34
36
|
when String, Symbol
|
35
|
-
return
|
37
|
+
return obj.to_sym
|
36
38
|
when Class
|
37
|
-
Devise.mappings.each_value { |m| return m.name if
|
39
|
+
Devise.mappings.each_value { |m| return m.name if obj <= m.to }
|
38
40
|
else
|
39
|
-
Devise.mappings.each_value { |m| return m.name if
|
41
|
+
Devise.mappings.each_value { |m| return m.name if obj.is_a?(m.to) }
|
40
42
|
end
|
41
43
|
|
42
|
-
raise "Could not find a valid mapping for #{
|
44
|
+
raise "Could not find a valid mapping for #{obj.inspect}"
|
43
45
|
end
|
44
46
|
|
45
47
|
def self.find_by_path!(path, path_type=:fullpath)
|
@@ -60,6 +62,8 @@ module Devise
|
|
60
62
|
@sign_out_via = options[:sign_out_via] || Devise.sign_out_via
|
61
63
|
@format = options[:format]
|
62
64
|
|
65
|
+
@router_name = options[:router_name]
|
66
|
+
|
63
67
|
default_failure_app(options)
|
64
68
|
default_controllers(options)
|
65
69
|
default_path_names(options)
|
@@ -1,4 +1,6 @@
|
|
1
|
+
require 'active_model/version'
|
1
2
|
require 'devise/hooks/activatable'
|
3
|
+
require 'devise/hooks/csrf_cleaner'
|
2
4
|
|
3
5
|
module Devise
|
4
6
|
module Models
|
@@ -10,32 +12,33 @@ module Devise
|
|
10
12
|
#
|
11
13
|
# * +authentication_keys+: parameters used for authentication. By default [:email].
|
12
14
|
#
|
15
|
+
# * +http_authentication_key+: map the username passed via HTTP Auth to this parameter. Defaults to
|
16
|
+
# the first element in +authentication_keys+.
|
17
|
+
#
|
13
18
|
# * +request_keys+: parameters from the request object used for authentication.
|
14
19
|
# By specifying a symbol (which should be a request method), it will automatically be
|
15
20
|
# passed to find_for_authentication method and considered in your model lookup.
|
16
21
|
#
|
17
22
|
# For instance, if you set :request_keys to [:subdomain], :subdomain will be considered
|
18
|
-
# as key on authentication. This can also be a hash where the value is a boolean
|
23
|
+
# as key on authentication. This can also be a hash where the value is a boolean specifying
|
19
24
|
# if the value is required or not.
|
20
25
|
#
|
21
|
-
# * +http_authenticatable+: if this model allows http authentication. By default
|
26
|
+
# * +http_authenticatable+: if this model allows http authentication. By default false.
|
22
27
|
# It also accepts an array specifying the strategies that should allow http.
|
23
28
|
#
|
24
29
|
# * +params_authenticatable+: if this model allows authentication through request params. By default true.
|
25
30
|
# It also accepts an array specifying the strategies that should allow params authentication.
|
26
31
|
#
|
27
32
|
# * +skip_session_storage+: By default Devise will store the user in session.
|
28
|
-
#
|
29
|
-
# :skip_session_storage => [:token_auth] or :skip_session_storage => [:http_auth, :token_auth],
|
30
|
-
# by default is set to :skip_session_storage => [:http_auth].
|
33
|
+
# By default is set to skip_session_storage: [:http_auth].
|
31
34
|
#
|
32
35
|
# == active_for_authentication?
|
33
36
|
#
|
34
37
|
# After authenticating a user and in each request, Devise checks if your model is active by
|
35
|
-
# calling model.active_for_authentication?. This method is
|
38
|
+
# calling model.active_for_authentication?. This method is overwritten by other devise modules. For instance,
|
36
39
|
# :confirmable overwrites .active_for_authentication? to only return true if your model was confirmed.
|
37
40
|
#
|
38
|
-
# You overwrite this method yourself, but if you do, don't forget to call super:
|
41
|
+
# You can overwrite this method yourself, but if you do, don't forget to call super:
|
39
42
|
#
|
40
43
|
# def active_for_authentication?
|
41
44
|
# super && special_condition_is_valid?
|
@@ -54,10 +57,10 @@ module Devise
|
|
54
57
|
BLACKLIST_FOR_SERIALIZATION = [:encrypted_password, :reset_password_token, :reset_password_sent_at,
|
55
58
|
:remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip,
|
56
59
|
:last_sign_in_ip, :password_salt, :confirmation_token, :confirmed_at, :confirmation_sent_at,
|
57
|
-
:remember_token, :unconfirmed_email, :failed_attempts, :unlock_token, :locked_at
|
60
|
+
:remember_token, :unconfirmed_email, :failed_attempts, :unlock_token, :locked_at]
|
58
61
|
|
59
62
|
included do
|
60
|
-
class_attribute :devise_modules, :
|
63
|
+
class_attribute :devise_modules, instance_writer: false
|
61
64
|
self.devise_modules ||= []
|
62
65
|
|
63
66
|
before_validation :downcase_keys
|
@@ -93,33 +96,22 @@ module Devise
|
|
93
96
|
def authenticatable_salt
|
94
97
|
end
|
95
98
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
+
# Redefine serializable_hash in models for more secure defaults.
|
100
|
+
# By default, it removes from the serializable model all attributes that
|
101
|
+
# are *not* accessible. You can remove this default by using :force_except
|
102
|
+
# and passing a new list of attributes you want to exempt. All attributes
|
103
|
+
# given to :except will simply add names to exempt to Devise internal list.
|
104
|
+
def serializable_hash(options = nil)
|
105
|
+
options ||= {}
|
106
|
+
options[:except] = Array(options[:except])
|
107
|
+
|
108
|
+
if options[:force_except]
|
109
|
+
options[:except].concat Array(options[:force_except])
|
110
|
+
else
|
111
|
+
options[:except].concat BLACKLIST_FOR_SERIALIZATION
|
112
|
+
end
|
99
113
|
|
100
|
-
|
101
|
-
# to_xml does not call serializable_hash on 3.1
|
102
|
-
array << "to_xml" if Rails::VERSION::STRING[0,3] == "3.1"
|
103
|
-
|
104
|
-
array.each do |method|
|
105
|
-
class_eval <<-RUBY, __FILE__, __LINE__
|
106
|
-
# Redefine to_xml and serializable_hash in models for more secure defaults.
|
107
|
-
# By default, it removes from the serializable model all attributes that
|
108
|
-
# are *not* accessible. You can remove this default by using :force_except
|
109
|
-
# and passing a new list of attributes you want to exempt. All attributes
|
110
|
-
# given to :except will simply add names to exempt to Devise internal list.
|
111
|
-
def #{method}(options=nil)
|
112
|
-
options ||= {}
|
113
|
-
options[:except] = Array(options[:except])
|
114
|
-
|
115
|
-
if options[:force_except]
|
116
|
-
options[:except].concat Array(options[:force_except])
|
117
|
-
else
|
118
|
-
options[:except].concat BLACKLIST_FOR_SERIALIZATION
|
119
|
-
end
|
120
|
-
super(options)
|
121
|
-
end
|
122
|
-
RUBY
|
114
|
+
super(options)
|
123
115
|
end
|
124
116
|
|
125
117
|
protected
|
@@ -129,7 +121,7 @@ module Devise
|
|
129
121
|
end
|
130
122
|
|
131
123
|
# This is an internal method called every time Devise needs
|
132
|
-
# to send a notification/mail. This can be
|
124
|
+
# to send a notification/mail. This can be overridden if you
|
133
125
|
# need to customize the e-mail delivery logic. For instance,
|
134
126
|
# if you are using a queue to deliver e-mails (delayed job,
|
135
127
|
# sidekiq, resque, etc), you must add the delivery to the queue
|
@@ -144,14 +136,26 @@ module Devise
|
|
144
136
|
#
|
145
137
|
# protected
|
146
138
|
#
|
147
|
-
# def send_devise_notification(notification)
|
148
|
-
#
|
139
|
+
# def send_devise_notification(notification, *args)
|
140
|
+
# # If the record is new or changed then delay the
|
141
|
+
# # delivery until the after_commit callback otherwise
|
142
|
+
# # send now because after_commit will not be called.
|
143
|
+
# if new_record? || changed?
|
144
|
+
# pending_notifications << [notification, args]
|
145
|
+
# else
|
146
|
+
# devise_mailer.send(notification, self, *args).deliver
|
147
|
+
# end
|
149
148
|
# end
|
150
149
|
#
|
151
150
|
# def send_pending_notifications
|
152
|
-
# pending_notifications.each do |
|
153
|
-
# devise_mailer.send(
|
151
|
+
# pending_notifications.each do |notification, args|
|
152
|
+
# devise_mailer.send(notification, self, *args).deliver
|
154
153
|
# end
|
154
|
+
#
|
155
|
+
# # Empty the pending notifications array because the
|
156
|
+
# # after_commit hook can be called multiple times which
|
157
|
+
# # could cause multiple emails to be sent.
|
158
|
+
# pending_notifications.clear
|
155
159
|
# end
|
156
160
|
#
|
157
161
|
# def pending_notifications
|
@@ -159,21 +163,42 @@ module Devise
|
|
159
163
|
# end
|
160
164
|
# end
|
161
165
|
#
|
162
|
-
def send_devise_notification(notification)
|
163
|
-
devise_mailer.send(notification, self)
|
166
|
+
def send_devise_notification(notification, *args)
|
167
|
+
message = devise_mailer.send(notification, self, *args)
|
168
|
+
# Remove once we move to Rails 4.2+ only.
|
169
|
+
if message.respond_to?(:deliver_now)
|
170
|
+
message.deliver_now
|
171
|
+
else
|
172
|
+
message.deliver
|
173
|
+
end
|
164
174
|
end
|
165
175
|
|
166
176
|
def downcase_keys
|
167
|
-
self.class.case_insensitive_keys.each { |k|
|
177
|
+
self.class.case_insensitive_keys.each { |k| apply_to_attribute_or_variable(k, :downcase) }
|
168
178
|
end
|
169
179
|
|
170
180
|
def strip_whitespace
|
171
|
-
self.class.strip_whitespace_keys.each { |k|
|
181
|
+
self.class.strip_whitespace_keys.each { |k| apply_to_attribute_or_variable(k, :strip) }
|
182
|
+
end
|
183
|
+
|
184
|
+
def apply_to_attribute_or_variable(attr, method)
|
185
|
+
if self[attr]
|
186
|
+
self[attr] = self[attr].try(method)
|
187
|
+
|
188
|
+
# Use respond_to? here to avoid a regression where globally
|
189
|
+
# configured strip_whitespace_keys or case_insensitive_keys were
|
190
|
+
# attempting to strip or downcase when a model didn't have the
|
191
|
+
# globally configured key.
|
192
|
+
elsif respond_to?(attr) && respond_to?("#{attr}=")
|
193
|
+
new_value = send(attr).try(method)
|
194
|
+
send("#{attr}=", new_value)
|
195
|
+
end
|
172
196
|
end
|
173
197
|
|
174
198
|
module ClassMethods
|
175
199
|
Devise::Models.config(self, :authentication_keys, :request_keys, :strip_whitespace_keys,
|
176
|
-
:case_insensitive_keys, :http_authenticatable, :params_authenticatable, :skip_session_storage
|
200
|
+
:case_insensitive_keys, :http_authenticatable, :params_authenticatable, :skip_session_storage,
|
201
|
+
:http_authentication_key)
|
177
202
|
|
178
203
|
def serialize_into_session(record)
|
179
204
|
[record.to_key, record.authenticatable_salt]
|
@@ -199,37 +224,36 @@ module Devise
|
|
199
224
|
# it may be wrapped as well. For instance, database authenticatable
|
200
225
|
# provides a `find_for_database_authentication` that wraps a call to
|
201
226
|
# this method. This allows you to customize both database authenticatable
|
202
|
-
# or the whole authenticate stack by customize `find_for_authentication.`
|
227
|
+
# or the whole authenticate stack by customize `find_for_authentication.`
|
203
228
|
#
|
204
229
|
# Overwrite to add customized conditions, create a join, or maybe use a
|
205
230
|
# namedscope to filter records while authenticating.
|
206
231
|
# Example:
|
207
232
|
#
|
208
|
-
# def self.find_for_authentication(
|
209
|
-
#
|
210
|
-
# super
|
233
|
+
# def self.find_for_authentication(tainted_conditions)
|
234
|
+
# find_first_by_auth_conditions(tainted_conditions, active: true)
|
211
235
|
# end
|
212
236
|
#
|
213
237
|
# Finally, notice that Devise also queries for users in other scenarios
|
214
238
|
# besides authentication, for example when retrieving an user to send
|
215
239
|
# an e-mail for password reset. In such cases, find_for_authentication
|
216
240
|
# is not called.
|
217
|
-
def find_for_authentication(
|
218
|
-
find_first_by_auth_conditions(
|
241
|
+
def find_for_authentication(tainted_conditions)
|
242
|
+
find_first_by_auth_conditions(tainted_conditions)
|
219
243
|
end
|
220
244
|
|
221
|
-
def find_first_by_auth_conditions(
|
222
|
-
to_adapter.find_first
|
245
|
+
def find_first_by_auth_conditions(tainted_conditions, opts={})
|
246
|
+
to_adapter.find_first(devise_parameter_filter.filter(tainted_conditions).merge(opts))
|
223
247
|
end
|
224
248
|
|
225
|
-
# Find
|
249
|
+
# Find or initialize a record setting an error if it can't be found.
|
226
250
|
def find_or_initialize_with_error_by(attribute, value, error=:invalid) #:nodoc:
|
227
251
|
find_or_initialize_with_errors([attribute], { attribute => value }, error)
|
228
252
|
end
|
229
253
|
|
230
|
-
# Find
|
254
|
+
# Find or initialize a record with group of attributes based on a list of required attributes.
|
231
255
|
def find_or_initialize_with_errors(required_attributes, attributes, error=:invalid) #:nodoc:
|
232
|
-
attributes = attributes.slice(*required_attributes)
|
256
|
+
attributes = attributes.slice(*required_attributes).with_indifferent_access
|
233
257
|
attributes.delete_if { |key, value| value.blank? }
|
234
258
|
|
235
259
|
if attributes.size == required_attributes.size
|
@@ -251,16 +275,8 @@ module Devise
|
|
251
275
|
|
252
276
|
protected
|
253
277
|
|
254
|
-
def
|
255
|
-
@
|
256
|
-
end
|
257
|
-
|
258
|
-
# Generate a token by looping and ensuring does not already exist.
|
259
|
-
def generate_token(column)
|
260
|
-
loop do
|
261
|
-
token = Devise.friendly_token
|
262
|
-
break token unless to_adapter.find_first({ column => token })
|
263
|
-
end
|
278
|
+
def devise_parameter_filter
|
279
|
+
@devise_parameter_filter ||= Devise::ParameterFilter.new(case_insensitive_keys, strip_whitespace_keys)
|
264
280
|
end
|
265
281
|
end
|
266
282
|
end
|
@@ -5,35 +5,56 @@ module Devise
|
|
5
5
|
# Confirmation instructions are sent to the user email after creating a
|
6
6
|
# record and when manually requested by a new confirmation instruction request.
|
7
7
|
#
|
8
|
+
# Confirmable tracks the following columns:
|
9
|
+
#
|
10
|
+
# * confirmation_token - A unique random token
|
11
|
+
# * confirmed_at - A timestamp when the user clicked the confirmation link
|
12
|
+
# * confirmation_sent_at - A timestamp when the confirmation_token was generated (not sent)
|
13
|
+
# * unconfirmed_email - An email address copied from the email attr. After confirmation
|
14
|
+
# this value is copied to the email attr then cleared
|
15
|
+
#
|
8
16
|
# == Options
|
9
17
|
#
|
10
|
-
# Confirmable adds the following options to
|
18
|
+
# Confirmable adds the following options to +devise+:
|
11
19
|
#
|
12
|
-
# * +allow_unconfirmed_access_for+: the time you want to allow the user to access
|
20
|
+
# * +allow_unconfirmed_access_for+: the time you want to allow the user to access their account
|
13
21
|
# before confirming it. After this period, the user access is denied. You can
|
14
22
|
# use this to let your user access some features of your application without
|
15
23
|
# confirming the account, but blocking it after a certain period (ie 7 days).
|
16
24
|
# By default allow_unconfirmed_access_for is zero, it means users always have to confirm to sign in.
|
17
25
|
# * +reconfirmable+: requires any email changes to be confirmed (exactly the same way as
|
18
26
|
# initial account confirmation) to be applied. Requires additional unconfirmed_email
|
19
|
-
# db field to be setup (t.reconfirmable in migrations). Until confirmed new email is
|
27
|
+
# db field to be setup (t.reconfirmable in migrations). Until confirmed, new email is
|
20
28
|
# stored in unconfirmed email column, and copied to email column on successful
|
21
29
|
# confirmation.
|
30
|
+
# * +confirm_within+: the time before a sent confirmation token becomes invalid.
|
31
|
+
# You can use this to force the user to confirm within a set period of time.
|
32
|
+
# Confirmable will not generate a new token if a repeat confirmation is requested
|
33
|
+
# during this time frame, unless the user's email changed too.
|
22
34
|
#
|
23
35
|
# == Examples
|
24
36
|
#
|
25
|
-
# User.find(1).confirm
|
37
|
+
# User.find(1).confirm # returns true unless it's already confirmed
|
26
38
|
# User.find(1).confirmed? # true/false
|
27
39
|
# User.find(1).send_confirmation_instructions # manually send instructions
|
28
40
|
#
|
29
41
|
module Confirmable
|
30
42
|
extend ActiveSupport::Concern
|
43
|
+
include ActionView::Helpers::DateHelper
|
31
44
|
|
32
45
|
included do
|
33
|
-
before_create :generate_confirmation_token, :
|
34
|
-
after_create :send_on_create_confirmation_instructions, :
|
35
|
-
before_update :
|
36
|
-
after_update :
|
46
|
+
before_create :generate_confirmation_token, if: :confirmation_required?
|
47
|
+
after_create :send_on_create_confirmation_instructions, if: :send_confirmation_notification?
|
48
|
+
before_update :postpone_email_change_until_confirmation_and_regenerate_confirmation_token, if: :postpone_email_change?
|
49
|
+
after_update :send_reconfirmation_instructions, if: :reconfirmation_required?
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(*args, &block)
|
53
|
+
@bypass_confirmation_postpone = false
|
54
|
+
@reconfirmation_required = false
|
55
|
+
@skip_confirmation_notification = false
|
56
|
+
@raw_confirmation_token = nil
|
57
|
+
super
|
37
58
|
end
|
38
59
|
|
39
60
|
def self.required_fields(klass)
|
@@ -45,24 +66,37 @@ module Devise
|
|
45
66
|
# Confirm a user by setting it's confirmed_at to actual time. If the user
|
46
67
|
# is already confirmed, add an error to email field. If the user is invalid
|
47
68
|
# add errors
|
48
|
-
def confirm
|
69
|
+
def confirm(args={})
|
49
70
|
pending_any_confirmation do
|
50
|
-
|
71
|
+
if confirmation_period_expired?
|
72
|
+
self.errors.add(:email, :confirmation_period_expired,
|
73
|
+
period: Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago))
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
|
51
77
|
self.confirmed_at = Time.now.utc
|
52
78
|
|
53
|
-
if self.class.reconfirmable && unconfirmed_email.present?
|
79
|
+
saved = if self.class.reconfirmable && unconfirmed_email.present?
|
54
80
|
skip_reconfirmation!
|
55
81
|
self.email = unconfirmed_email
|
56
82
|
self.unconfirmed_email = nil
|
57
83
|
|
58
84
|
# We need to validate in such cases to enforce e-mail uniqueness
|
59
|
-
save(:
|
85
|
+
save(validate: true)
|
60
86
|
else
|
61
|
-
save(:
|
87
|
+
save(validate: args[:ensure_valid] == true)
|
62
88
|
end
|
89
|
+
|
90
|
+
after_confirmation if saved
|
91
|
+
saved
|
63
92
|
end
|
64
93
|
end
|
65
94
|
|
95
|
+
def confirm!(args={})
|
96
|
+
ActiveSupport::Deprecation.warn "confirm! is deprecated in favor of confirm"
|
97
|
+
confirm(args)
|
98
|
+
end
|
99
|
+
|
66
100
|
# Verifies whether a user is confirmed or not
|
67
101
|
def confirmed?
|
68
102
|
!!confirmed_at
|
@@ -74,16 +108,28 @@ module Devise
|
|
74
108
|
|
75
109
|
# Send confirmation instructions by email
|
76
110
|
def send_confirmation_instructions
|
77
|
-
|
111
|
+
unless @raw_confirmation_token
|
112
|
+
generate_confirmation_token!
|
113
|
+
end
|
114
|
+
|
115
|
+
opts = pending_reconfirmation? ? { to: unconfirmed_email } : { }
|
116
|
+
send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
|
117
|
+
end
|
118
|
+
|
119
|
+
def send_reconfirmation_instructions
|
78
120
|
@reconfirmation_required = false
|
79
121
|
|
80
|
-
|
81
|
-
|
122
|
+
unless @skip_confirmation_notification
|
123
|
+
send_confirmation_instructions
|
124
|
+
end
|
82
125
|
end
|
83
126
|
|
84
|
-
# Resend confirmation token.
|
85
|
-
|
86
|
-
|
127
|
+
# Resend confirmation token.
|
128
|
+
# Regenerates the token if the period is expired.
|
129
|
+
def resend_confirmation_instructions
|
130
|
+
pending_any_confirmation do
|
131
|
+
send_confirmation_instructions
|
132
|
+
end
|
87
133
|
end
|
88
134
|
|
89
135
|
# Overwrites active_for_authentication? for confirmation
|
@@ -105,27 +151,26 @@ module Devise
|
|
105
151
|
self.confirmed_at = Time.now.utc
|
106
152
|
end
|
107
153
|
|
154
|
+
# Skips sending the confirmation/reconfirmation notification email after_create/after_update. Unlike
|
155
|
+
# #skip_confirmation!, record still requires confirmation.
|
156
|
+
def skip_confirmation_notification!
|
157
|
+
@skip_confirmation_notification = true
|
158
|
+
end
|
159
|
+
|
108
160
|
# If you don't want reconfirmation to be sent, neither a code
|
109
161
|
# to be generated, call skip_reconfirmation!
|
110
162
|
def skip_reconfirmation!
|
111
|
-
@
|
112
|
-
end
|
113
|
-
|
114
|
-
def headers_for(action)
|
115
|
-
headers = super
|
116
|
-
if action == :confirmation_instructions && pending_reconfirmation?
|
117
|
-
headers[:to] = unconfirmed_email
|
118
|
-
end
|
119
|
-
headers
|
163
|
+
@bypass_confirmation_postpone = true
|
120
164
|
end
|
121
165
|
|
122
166
|
protected
|
123
167
|
|
124
168
|
# A callback method used to deliver confirmation
|
125
|
-
# instructions on creation. This can be
|
169
|
+
# instructions on creation. This can be overridden
|
126
170
|
# in models to map to a nice sign up e-mail.
|
127
171
|
def send_on_create_confirmation_instructions
|
128
|
-
|
172
|
+
send_confirmation_instructions
|
173
|
+
skip_reconfirmation!
|
129
174
|
end
|
130
175
|
|
131
176
|
# Callback to overwrite if confirmation is required or not.
|
@@ -152,13 +197,32 @@ module Devise
|
|
152
197
|
# # allow_unconfirmed_access_for = 0.days
|
153
198
|
# confirmation_period_valid? # will always return false
|
154
199
|
#
|
200
|
+
# # allow_unconfirmed_access_for = nil
|
201
|
+
# confirmation_period_valid? # will always return true
|
202
|
+
#
|
155
203
|
def confirmation_period_valid?
|
156
|
-
confirmation_sent_at && confirmation_sent_at.utc >= self.class.allow_unconfirmed_access_for.ago
|
204
|
+
self.class.allow_unconfirmed_access_for.nil? || (confirmation_sent_at && confirmation_sent_at.utc >= self.class.allow_unconfirmed_access_for.ago)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Checks if the user confirmation happens before the token becomes invalid
|
208
|
+
# Examples:
|
209
|
+
#
|
210
|
+
# # confirm_within = 3.days and confirmation_sent_at = 2.days.ago
|
211
|
+
# confirmation_period_expired? # returns false
|
212
|
+
#
|
213
|
+
# # confirm_within = 3.days and confirmation_sent_at = 4.days.ago
|
214
|
+
# confirmation_period_expired? # returns true
|
215
|
+
#
|
216
|
+
# # confirm_within = nil
|
217
|
+
# confirmation_period_expired? # will always return false
|
218
|
+
#
|
219
|
+
def confirmation_period_expired?
|
220
|
+
self.class.confirm_within && self.confirmation_sent_at && (Time.now > self.confirmation_sent_at + self.class.confirm_within)
|
157
221
|
end
|
158
222
|
|
159
223
|
# Checks whether the record requires any confirmation.
|
160
224
|
def pending_any_confirmation
|
161
|
-
if !confirmed? || pending_reconfirmation?
|
225
|
+
if (!confirmed? || pending_reconfirmation?)
|
162
226
|
yield
|
163
227
|
else
|
164
228
|
self.errors.add(:email, :already_confirmed)
|
@@ -166,36 +230,55 @@ module Devise
|
|
166
230
|
end
|
167
231
|
end
|
168
232
|
|
169
|
-
# Generates a new random token for confirmation, and stores
|
170
|
-
# this token is being generated
|
233
|
+
# Generates a new random token for confirmation, and stores
|
234
|
+
# the time this token is being generated in confirmation_sent_at
|
171
235
|
def generate_confirmation_token
|
172
|
-
self.confirmation_token
|
173
|
-
|
236
|
+
if self.confirmation_token && !confirmation_period_expired?
|
237
|
+
@raw_confirmation_token = self.confirmation_token
|
238
|
+
else
|
239
|
+
raw, _ = Devise.token_generator.generate(self.class, :confirmation_token)
|
240
|
+
self.confirmation_token = @raw_confirmation_token = raw
|
241
|
+
self.confirmation_sent_at = Time.now.utc
|
242
|
+
end
|
174
243
|
end
|
175
244
|
|
176
245
|
def generate_confirmation_token!
|
177
|
-
generate_confirmation_token && save(:
|
178
|
-
end
|
179
|
-
|
180
|
-
def after_password_reset
|
181
|
-
super
|
182
|
-
confirm! unless confirmed?
|
246
|
+
generate_confirmation_token && save(validate: false)
|
183
247
|
end
|
184
248
|
|
185
|
-
def
|
249
|
+
def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
|
186
250
|
@reconfirmation_required = true
|
187
251
|
self.unconfirmed_email = self.email
|
188
252
|
self.email = self.email_was
|
253
|
+
self.confirmation_token = nil
|
254
|
+
generate_confirmation_token
|
189
255
|
end
|
190
256
|
|
191
257
|
def postpone_email_change?
|
192
|
-
postpone = self.class.reconfirmable && email_changed? && !@
|
193
|
-
@
|
258
|
+
postpone = self.class.reconfirmable && email_changed? && !@bypass_confirmation_postpone && self.email.present?
|
259
|
+
@bypass_confirmation_postpone = false
|
194
260
|
postpone
|
195
261
|
end
|
196
262
|
|
197
263
|
def reconfirmation_required?
|
198
|
-
self.class.reconfirmable && @reconfirmation_required
|
264
|
+
self.class.reconfirmable && @reconfirmation_required && (self.email.present? || self.unconfirmed_email.present?)
|
265
|
+
end
|
266
|
+
|
267
|
+
def send_confirmation_notification?
|
268
|
+
confirmation_required? && !@skip_confirmation_notification && self.email.present?
|
269
|
+
end
|
270
|
+
|
271
|
+
# A callback initiated after successfully confirming. This can be
|
272
|
+
# used to insert your own logic that is only run after the user successfully
|
273
|
+
# confirms.
|
274
|
+
#
|
275
|
+
# Example:
|
276
|
+
#
|
277
|
+
# def after_confirmation
|
278
|
+
# self.update_attribute(:invite_code, nil)
|
279
|
+
# end
|
280
|
+
#
|
281
|
+
def after_confirmation
|
199
282
|
end
|
200
283
|
|
201
284
|
module ClassMethods
|
@@ -208,7 +291,7 @@ module Devise
|
|
208
291
|
unless confirmable.try(:persisted?)
|
209
292
|
confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
|
210
293
|
end
|
211
|
-
confirmable.
|
294
|
+
confirmable.resend_confirmation_instructions if confirmable.persisted?
|
212
295
|
confirmable
|
213
296
|
end
|
214
297
|
|
@@ -217,14 +300,18 @@ module Devise
|
|
217
300
|
# If the user is already confirmed, create an error for the user
|
218
301
|
# Options must have the confirmation_token
|
219
302
|
def confirm_by_token(confirmation_token)
|
220
|
-
confirmable =
|
221
|
-
|
222
|
-
|
223
|
-
|
303
|
+
confirmable = find_first_by_auth_conditions(confirmation_token: confirmation_token)
|
304
|
+
unless confirmable
|
305
|
+
confirmation_digest = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
|
306
|
+
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_digest)
|
307
|
+
end
|
308
|
+
|
309
|
+
# TODO: replace above lines with
|
310
|
+
# confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
|
311
|
+
# after enough time has passed that Devise clients do not use digested tokens
|
224
312
|
|
225
|
-
|
226
|
-
|
227
|
-
generate_token(:confirmation_token)
|
313
|
+
confirmable.confirm if confirmable.persisted?
|
314
|
+
confirmable
|
228
315
|
end
|
229
316
|
|
230
317
|
# Find a record for confirmation by unconfirmed email field
|
@@ -235,7 +322,7 @@ module Devise
|
|
235
322
|
find_or_initialize_with_errors(unconfirmed_required_attributes, unconfirmed_attributes, :not_found)
|
236
323
|
end
|
237
324
|
|
238
|
-
Devise::Models.config(self, :allow_unconfirmed_access_for, :confirmation_keys, :reconfirmable)
|
325
|
+
Devise::Models.config(self, :allow_unconfirmed_access_for, :confirmation_keys, :reconfirmable, :confirm_within)
|
239
326
|
end
|
240
327
|
end
|
241
328
|
end
|