devise 3.5.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +14 -15
  3. data/CHANGELOG.md +57 -1125
  4. data/CODE_OF_CONDUCT.md +22 -0
  5. data/CONTRIBUTING.md +2 -0
  6. data/Gemfile +3 -3
  7. data/Gemfile.lock +80 -80
  8. data/MIT-LICENSE +1 -1
  9. data/README.md +27 -18
  10. data/app/controllers/devise/confirmations_controller.rb +1 -1
  11. data/app/controllers/devise/omniauth_callbacks_controller.rb +4 -4
  12. data/app/controllers/devise/passwords_controller.rb +5 -4
  13. data/app/controllers/devise/registrations_controller.rb +5 -5
  14. data/app/controllers/devise/sessions_controller.rb +7 -7
  15. data/app/controllers/devise/unlocks_controller.rb +2 -2
  16. data/app/controllers/devise_controller.rb +20 -9
  17. data/app/mailers/devise/mailer.rb +4 -0
  18. data/app/views/devise/confirmations/new.html.erb +1 -1
  19. data/app/views/devise/mailer/password_change.html.erb +3 -0
  20. data/app/views/devise/shared/_links.html.erb +1 -1
  21. data/bin/test +13 -0
  22. data/config/locales/en.yml +2 -0
  23. data/devise.gemspec +2 -3
  24. data/gemfiles/Gemfile.rails-4.1-stable +4 -4
  25. data/gemfiles/Gemfile.rails-4.1-stable.lock +69 -71
  26. data/gemfiles/Gemfile.rails-4.2-stable +4 -4
  27. data/gemfiles/Gemfile.rails-4.2-stable.lock +81 -83
  28. data/gemfiles/{Gemfile.rails-3.2-stable → Gemfile.rails-5.0-beta} +15 -7
  29. data/gemfiles/Gemfile.rails-5.0-beta.lock +199 -0
  30. data/lib/devise/controllers/helpers.rb +20 -24
  31. data/lib/devise/controllers/rememberable.rb +8 -1
  32. data/lib/devise/encryptor.rb +4 -4
  33. data/lib/devise/failure_app.rb +33 -12
  34. data/lib/devise/hooks/timeoutable.rb +5 -3
  35. data/lib/devise/mailers/helpers.rb +1 -1
  36. data/lib/devise/models/authenticatable.rb +5 -1
  37. data/lib/devise/models/confirmable.rb +6 -6
  38. data/lib/devise/models/database_authenticatable.rb +20 -7
  39. data/lib/devise/models/lockable.rb +1 -1
  40. data/lib/devise/models/recoverable.rb +3 -7
  41. data/lib/devise/models/rememberable.rb +38 -24
  42. data/lib/devise/models/timeoutable.rb +0 -6
  43. data/lib/devise/models.rb +1 -1
  44. data/lib/devise/omniauth/url_helpers.rb +62 -4
  45. data/lib/devise/parameter_sanitizer.rb +176 -61
  46. data/lib/devise/rails/routes.rb +54 -31
  47. data/lib/devise/rails/warden_compat.rb +1 -10
  48. data/lib/devise/rails.rb +1 -10
  49. data/lib/devise/strategies/authenticatable.rb +1 -1
  50. data/lib/devise/strategies/database_authenticatable.rb +3 -3
  51. data/lib/devise/strategies/rememberable.rb +3 -6
  52. data/lib/devise/test_helpers.rb +10 -5
  53. data/lib/devise/token_generator.rb +1 -41
  54. data/lib/devise/version.rb +1 -1
  55. data/lib/devise.rb +115 -20
  56. data/lib/generators/active_record/devise_generator.rb +11 -5
  57. data/lib/generators/active_record/templates/migration.rb +2 -2
  58. data/lib/generators/active_record/templates/migration_existing.rb +2 -2
  59. data/lib/generators/devise/install_generator.rb +15 -0
  60. data/lib/generators/devise/orm_helpers.rb +1 -18
  61. data/lib/generators/devise/views_generator.rb +14 -3
  62. data/lib/generators/templates/controllers/registrations_controller.rb +4 -4
  63. data/lib/generators/templates/controllers/sessions_controller.rb +2 -2
  64. data/lib/generators/templates/devise.rb +19 -17
  65. data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
  66. data/lib/generators/templates/markerb/password_change.markerb +3 -0
  67. data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
  68. data/lib/generators/templates/markerb/unlock_instructions.markerb +1 -1
  69. data/test/controllers/custom_registrations_controller_test.rb +5 -5
  70. data/test/controllers/custom_strategy_test.rb +7 -5
  71. data/test/controllers/helper_methods_test.rb +22 -0
  72. data/test/controllers/helpers_test.rb +1 -1
  73. data/test/controllers/inherited_controller_i18n_messages_test.rb +2 -2
  74. data/test/controllers/internal_helpers_test.rb +8 -10
  75. data/test/controllers/load_hooks_controller_test.rb +1 -1
  76. data/test/controllers/passwords_controller_test.rb +4 -3
  77. data/test/controllers/sessions_controller_test.rb +21 -18
  78. data/test/controllers/url_helpers_test.rb +1 -1
  79. data/test/devise_test.rb +27 -0
  80. data/test/failure_app_test.rb +37 -15
  81. data/test/generators/active_record_generator_test.rb +0 -26
  82. data/test/generators/install_generator_test.rb +14 -3
  83. data/test/generators/views_generator_test.rb +7 -0
  84. data/test/helpers/devise_helper_test.rb +1 -1
  85. data/test/integration/authenticatable_test.rb +18 -18
  86. data/test/integration/confirmable_test.rb +5 -5
  87. data/test/integration/database_authenticatable_test.rb +1 -1
  88. data/test/integration/http_authenticatable_test.rb +4 -5
  89. data/test/integration/lockable_test.rb +4 -3
  90. data/test/integration/omniauthable_test.rb +12 -10
  91. data/test/integration/recoverable_test.rb +10 -10
  92. data/test/integration/registerable_test.rb +9 -11
  93. data/test/integration/rememberable_test.rb +42 -7
  94. data/test/integration/timeoutable_test.rb +16 -4
  95. data/test/integration/trackable_test.rb +1 -1
  96. data/test/mailers/confirmation_instructions_test.rb +6 -6
  97. data/test/mailers/reset_password_instructions_test.rb +5 -5
  98. data/test/mailers/unlock_instructions_test.rb +5 -5
  99. data/test/models/confirmable_test.rb +25 -1
  100. data/test/models/database_authenticatable_test.rb +26 -6
  101. data/test/models/lockable_test.rb +1 -1
  102. data/test/models/recoverable_test.rb +23 -0
  103. data/test/models/rememberable_test.rb +48 -95
  104. data/test/models/validatable_test.rb +2 -10
  105. data/test/models_test.rb +15 -6
  106. data/test/omniauth/url_helpers_test.rb +4 -7
  107. data/test/orm/active_record.rb +6 -1
  108. data/test/parameter_sanitizer_test.rb +103 -53
  109. data/test/rails_app/app/active_record/user.rb +1 -0
  110. data/test/rails_app/app/active_record/user_without_email.rb +8 -0
  111. data/test/rails_app/app/controllers/admins_controller.rb +1 -1
  112. data/test/rails_app/app/controllers/application_controller.rb +2 -2
  113. data/test/rails_app/app/controllers/home_controller.rb +5 -1
  114. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +2 -2
  115. data/test/rails_app/app/controllers/users_controller.rb +5 -5
  116. data/test/rails_app/app/mongoid/user_without_email.rb +33 -0
  117. data/test/rails_app/config/application.rb +1 -1
  118. data/test/rails_app/config/boot.rb +4 -4
  119. data/test/rails_app/config/environments/test.rb +6 -1
  120. data/test/rails_app/config/initializers/devise.rb +1 -1
  121. data/test/rails_app/config/initializers/secret_token.rb +1 -6
  122. data/test/rails_app/config/routes.rb +5 -0
  123. data/test/rails_app/lib/shared_user_without_email.rb +26 -0
  124. data/test/routes_test.rb +28 -13
  125. data/test/support/helpers.rb +4 -0
  126. data/test/support/http_method_compatibility.rb +51 -0
  127. data/test/support/webrat/integrations/rails.rb +9 -0
  128. data/test/test_helpers_test.rb +5 -5
  129. data/test/test_models.rb +1 -1
  130. metadata +41 -45
  131. data/gemfiles/Gemfile.rails-3.2-stable.lock +0 -169
  132. data/gemfiles/Gemfile.rails-4.0-stable +0 -29
  133. data/gemfiles/Gemfile.rails-4.0-stable.lock +0 -163
  134. data/script/cached-bundle +0 -49
  135. data/script/s3-put +0 -71
@@ -16,10 +16,6 @@ module Devise
16
16
  # # resets the user password and save the record, true if valid passwords are given, otherwise false
17
17
  # User.find(1).reset_password('password123', 'password123')
18
18
  #
19
- # # only resets the user password, without saving the record
20
- # user = User.find(1)
21
- # user.reset_password('password123', 'password123')
22
- #
23
19
  # # creates a new token and send it with instructions about how to reset the password
24
20
  # User.find(1).send_reset_password_instructions
25
21
  #
@@ -31,8 +27,8 @@ module Devise
31
27
  end
32
28
 
33
29
  included do
34
- before_save do
35
- if email_changed? || encrypted_password_changed?
30
+ before_update do
31
+ if (respond_to?(:email_changed?) && email_changed?) || encrypted_password_changed?
36
32
  clear_reset_password_token
37
33
  end
38
34
  end
@@ -87,7 +83,7 @@ module Devise
87
83
  # reset_password_period_valid? # will always return false
88
84
  #
89
85
  def reset_password_period_valid?
90
- reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago
86
+ reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago.utc
91
87
  end
92
88
 
93
89
  protected
@@ -39,17 +39,15 @@ module Devise
39
39
  module Rememberable
40
40
  extend ActiveSupport::Concern
41
41
 
42
- attr_accessor :remember_me, :extend_remember_period
42
+ attr_accessor :remember_me
43
43
 
44
44
  def self.required_fields(klass)
45
45
  [:remember_created_at]
46
46
  end
47
47
 
48
- # Generate a new remember token and save the record without validations
49
- # if remember expired (token is no longer valid) or extend_remember_period is true
50
- def remember_me!(extend_period=false)
51
- self.remember_token = self.class.remember_token if generate_remember_token?
52
- self.remember_created_at = Time.now.utc if generate_remember_timestamp?(extend_period)
48
+ def remember_me!
49
+ self.remember_token = self.class.remember_token if respond_to?(:remember_token)
50
+ self.remember_created_at ||= Time.now.utc
53
51
  save(validate: false) if self.changed?
54
52
  end
55
53
 
@@ -57,19 +55,17 @@ module Devise
57
55
  # it exists), and save the record without validations.
58
56
  def forget_me!
59
57
  return unless persisted?
60
- self.remember_token = nil if respond_to?(:remember_token=)
58
+ self.remember_token = nil if respond_to?(:remember_token)
61
59
  self.remember_created_at = nil if self.class.expire_all_remember_me_on_sign_out
62
60
  save(validate: false)
63
61
  end
64
62
 
65
- # Remember token should be expired if expiration time not overpass now.
66
- def remember_expired?
67
- remember_created_at.nil? || (remember_expires_at <= Time.now.utc)
63
+ def remember_expires_at
64
+ self.class.remember_for.from_now
68
65
  end
69
66
 
70
- # Remember token expires at created time + remember_for configuration
71
- def remember_expires_at
72
- remember_created_at + self.class.remember_for
67
+ def extend_remember_period
68
+ self.class.extend_remember_period
73
69
  end
74
70
 
75
71
  def rememberable_value
@@ -102,29 +98,47 @@ module Devise
102
98
  def after_remembered
103
99
  end
104
100
 
105
- protected
101
+ def remember_me?(token, generated_at)
102
+ # TODO: Normalize the JSON type coercion along with the Timeoutable hook
103
+ # in a single place https://github.com/plataformatec/devise/blob/ffe9d6d406e79108cf32a2c6a1d0b3828849c40b/lib/devise/hooks/timeoutable.rb#L14-L18
104
+ if generated_at.is_a?(String)
105
+ generated_at = time_from_json(generated_at)
106
+ end
106
107
 
107
- def generate_remember_token? #:nodoc:
108
- respond_to?(:remember_token) && remember_expired?
108
+ # The token is only valid if:
109
+ # 1. we have a date
110
+ # 2. the current time does not pass the expiry period
111
+ # 3. the record has a remember_created_at date
112
+ # 4. the token date is bigger than the remember_created_at
113
+ # 5. the token matches
114
+ generated_at.is_a?(Time) &&
115
+ (self.class.remember_for.ago < generated_at) &&
116
+ (generated_at > (remember_created_at || Time.now).utc) &&
117
+ Devise.secure_compare(rememberable_value, token)
109
118
  end
110
119
 
111
- # Generate a timestamp if extend_remember_period is true, if no remember_token
112
- # exists, or if an existing remember token has expired.
113
- def generate_remember_timestamp?(extend_period) #:nodoc:
114
- extend_period || remember_expired?
120
+ private
121
+
122
+ def time_from_json(value)
123
+ if value =~ /\A\d+\.\d+\Z/
124
+ Time.at(value.to_f)
125
+ else
126
+ Time.parse(value) rescue nil
127
+ end
115
128
  end
116
129
 
117
130
  module ClassMethods
118
131
  # Create the cookie key using the record id and remember_token
119
132
  def serialize_into_cookie(record)
120
- [record.to_key, record.rememberable_value]
133
+ [record.to_key, record.rememberable_value, Time.now.utc.to_f.to_s]
121
134
  end
122
135
 
123
136
  # Recreate the user based on the stored cookie
124
- def serialize_from_cookie(id, remember_token)
137
+ def serialize_from_cookie(*args)
138
+ id, token, generated_at = *args
139
+
125
140
  record = to_adapter.get(id)
126
- record if record && !record.remember_expired? &&
127
- Devise.secure_compare(record.rememberable_value, remember_token)
141
+ record if record && record.remember_me?(token, generated_at)
128
142
  end
129
143
 
130
144
  # Generate a token checking if one does not already exist in the database.
@@ -26,7 +26,6 @@ module Devise
26
26
 
27
27
  # Checks whether the user session has expired based on configured time.
28
28
  def timedout?(last_access)
29
- return false if remember_exists_and_not_expired?
30
29
  !timeout_in.nil? && last_access && last_access <= timeout_in.ago
31
30
  end
32
31
 
@@ -36,11 +35,6 @@ module Devise
36
35
 
37
36
  private
38
37
 
39
- def remember_exists_and_not_expired?
40
- return false unless respond_to?(:remember_created_at) && respond_to?(:remember_expired?)
41
- remember_created_at && !remember_expired?
42
- end
43
-
44
38
  module ClassMethods
45
39
  Devise::Models.config(self, :timeout_in)
46
40
  end
data/lib/devise/models.rb CHANGED
@@ -12,7 +12,7 @@ module Devise
12
12
 
13
13
  # Creates configuration values for Devise and for the given module.
14
14
  #
15
- # Devise::Models.config(Devise::Authenticatable, :stretches, 10)
15
+ # Devise::Models.config(Devise::DatabaseAuthenticatable, :stretches)
16
16
  #
17
17
  # The line above creates:
18
18
  #
@@ -2,16 +2,74 @@ module Devise
2
2
  module OmniAuth
3
3
  module UrlHelpers
4
4
  def self.define_helpers(mapping)
5
+ return unless mapping.omniauthable?
6
+
7
+ mapping = mapping.name
8
+
9
+ class_eval do
10
+ define_method("#{mapping}_omniauth_authorize_path") do |provider, *args|
11
+ ActiveSupport::Deprecation.warn(<<-DEPRECATION.strip_heredoc)
12
+ [Devise] #{mapping}_omniauth_authorize_path(#{provider.inspect}) is deprecated and it will be removed from Devise 4.1.
13
+
14
+ Please use #{mapping}_#{provider}_omniauth_authorize_path instead.
15
+ DEPRECATION
16
+ send("#{mapping}_#{provider}_omniauth_authorize_path", *args)
17
+ end
18
+
19
+ define_method("#{mapping}_omniauth_authorize_url") do |provider, *args|
20
+ ActiveSupport::Deprecation.warn(<<-DEPRECATION.strip_heredoc)
21
+ [Devise] #{mapping}_omniauth_authorize_url(#{provider.inspect}) is deprecated and it will be removed from Devise 4.1.
22
+
23
+ Please use #{mapping}_#{provider}_omniauth_authorize_url instead.
24
+ DEPRECATION
25
+ send("#{mapping}_#{provider}_omniauth_authorize_url", *args)
26
+ end
27
+
28
+ define_method("#{mapping}_omniauth_callback_path") do |provider, *args|
29
+ ActiveSupport::Deprecation.warn(<<-DEPRECATION.strip_heredoc)
30
+ [Devise] #{mapping}_omniauth_callback_path(#{provider.inspect}) is deprecated and it will be removed from Devise 4.1.
31
+
32
+ Please use #{mapping}_#{provider}_omniauth_callback_path instead.
33
+ DEPRECATION
34
+ send("#{mapping}_#{provider}_omniauth_callback_path", *args)
35
+ end
36
+
37
+ define_method("#{mapping}_omniauth_callback_url") do |provider, *args|
38
+ ActiveSupport::Deprecation.warn(<<-DEPRECATION.strip_heredoc)
39
+ [Devise] #{mapping}_omniauth_callback_url(#{provider.inspect}) is deprecated and it will be removed from Devise 4.1.
40
+
41
+ Please use #{mapping}_#{provider}_omniauth_callback_url instead.
42
+ DEPRECATION
43
+ send("#{mapping}_#{provider}_omniauth_callback_url", *args)
44
+ end
45
+ end
46
+
47
+ ActiveSupport.on_load(:action_controller) do
48
+ if respond_to?(:helper_method)
49
+ helper_method "#{mapping}_omniauth_authorize_path", "#{mapping}_omniauth_authorize_url"
50
+ helper_method "#{mapping}_omniauth_callback_path", "#{mapping}_omniauth_callback_url"
51
+ end
52
+ end
53
+ end
54
+
55
+ def omniauth_authorize_path(resource_or_scope, provider, *args)
56
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
57
+ _devise_route_context.send("#{scope}_#{provider}_omniauth_authorize_path", *args)
58
+ end
59
+
60
+ def omniauth_authorize_url(resource_or_scope, provider, *args)
61
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
62
+ _devise_route_context.send("#{scope}_#{provider}_omniauth_authorize_url", *args)
5
63
  end
6
64
 
7
- def omniauth_authorize_path(resource_or_scope, *args)
65
+ def omniauth_callback_path(resource_or_scope, provider, *args)
8
66
  scope = Devise::Mapping.find_scope!(resource_or_scope)
9
- _devise_route_context.send("#{scope}_omniauth_authorize_path", *args)
67
+ _devise_route_context.send("#{scope}_#{provider}_omniauth_callback_path", *args)
10
68
  end
11
69
 
12
- def omniauth_callback_path(resource_or_scope, *args)
70
+ def omniauth_callback_url(resource_or_scope, provider, *args)
13
71
  scope = Devise::Mapping.find_scope!(resource_or_scope)
14
- _devise_route_context.send("#{scope}_omniauth_callback_path", *args)
72
+ _devise_route_context.send("#{scope}_#{provider}_omniauth_callback_url", *args)
15
73
  end
16
74
  end
17
75
  end
@@ -1,99 +1,214 @@
1
1
  module Devise
2
- class BaseSanitizer
3
- attr_reader :params, :resource_name, :resource_class
2
+ # The +ParameterSanitizer+ deals with permitting specific parameters values
3
+ # for each +Devise+ scope in the application.
4
+ #
5
+ # The sanitizer knows about Devise default parameters (like +password+ and
6
+ # +password_confirmation+ for the `RegistrationsController`), and you can
7
+ # extend or change the permitted parameters list on your controllers.
8
+ #
9
+ # === Permitting new parameters
10
+ #
11
+ # You can add new parameters to the permitted list using the +permit+ method
12
+ # in a +before_action+ method, for instance.
13
+ #
14
+ # class ApplicationController < ActionController::Base
15
+ # before_action :configure_permitted_parameters, if: :devise_controller?
16
+ #
17
+ # protected
18
+ #
19
+ # def configure_permitted_parameters
20
+ # # Permit the `subscribe_newsletter` parameter along with the other
21
+ # # sign up parameters.
22
+ # devise_parameter_sanitizer.permit(:sign_up, keys: [:subscribe_newsletter])
23
+ # end
24
+ # end
25
+ #
26
+ # Using a block yields an +ActionController::Parameters+ object so you can
27
+ # permit nested parameters and have more control over how the parameters are
28
+ # permitted in your controller.
29
+ #
30
+ # def configure_permitted_parameters
31
+ # devise_parameter_sanitizer.permit(:sign_up) do |user|
32
+ # user.permit(newsletter_preferences: [])
33
+ # end
34
+ # end
35
+ class ParameterSanitizer
36
+ DEFAULT_PERMITTED_ATTRIBUTES = {
37
+ sign_in: [:password, :remember_me],
38
+ sign_up: [:password, :password_confirmation],
39
+ account_update: [:password, :password_confirmation, :current_password]
40
+ }
4
41
 
5
42
  def initialize(resource_class, resource_name, params)
6
- @resource_class = resource_class
7
- @resource_name = resource_name
43
+ @auth_keys = extract_auth_keys(resource_class)
8
44
  @params = params
9
- @blocks = Hash.new
45
+ @resource_name = resource_name
46
+ @permitted = {}
47
+
48
+ DEFAULT_PERMITTED_ATTRIBUTES.each_pair do |action, keys|
49
+ permit(action, keys: keys)
50
+ end
10
51
  end
11
52
 
12
- def for(kind, &block)
13
- if block_given?
14
- @blocks[kind] = block
53
+ # Sanitize the parameters for a specific +action+.
54
+ #
55
+ # === Arguments
56
+ #
57
+ # * +action+ - A +Symbol+ with the action that the controller is
58
+ # performing, like +sign_up+, +sign_in+, etc.
59
+ #
60
+ # === Examples
61
+ #
62
+ # # Inside the `RegistrationsController#create` action.
63
+ # resource = build_resource(devise_parameter_sanitizer.sanitize(:sign_up))
64
+ # resource.save
65
+ #
66
+ # Returns an +ActiveSupport::HashWithIndifferentAccess+ with the permitted
67
+ # attributes.
68
+ def sanitize(action)
69
+ permissions = @permitted[action]
70
+
71
+ # DEPRECATED: Remove this branch on Devise 4.1.
72
+ if respond_to?(action, true)
73
+ deprecate_instance_method_sanitization(action)
74
+ return cast_to_hash send(action)
75
+ end
76
+
77
+ if permissions.respond_to?(:call)
78
+ cast_to_hash permissions.call(default_params)
79
+ elsif permissions.present?
80
+ cast_to_hash permit_keys(default_params, permissions)
15
81
  else
16
- default_for(kind)
82
+ unknown_action!(action)
17
83
  end
18
84
  end
19
85
 
20
- def sanitize(kind)
21
- if block = @blocks[kind]
22
- block.call(default_params)
86
+ # Add or remove new parameters to the permitted list of an +action+.
87
+ #
88
+ # === Arguments
89
+ #
90
+ # * +action+ - A +Symbol+ with the action that the controller is
91
+ # performing, like +sign_up+, +sign_in+, etc.
92
+ # * +keys:+ - An +Array+ of keys that also should be permitted.
93
+ # * +except:+ - An +Array+ of keys that shouldn't be permitted.
94
+ # * +block+ - A block that should be used to permit the action
95
+ # parameters instead of the +Array+ based approach. The block will be
96
+ # called with an +ActionController::Parameters+ instance.
97
+ #
98
+ # === Examples
99
+ #
100
+ # # Adding new parameters to be permitted in the `sign_up` action.
101
+ # devise_parameter_sanitizer.permit(:sign_up, keys: [:subscribe_newsletter])
102
+ #
103
+ # # Removing the `password` parameter from the `account_update` action.
104
+ # devise_parameter_sanitizer.permit(:account_update, except: [:password])
105
+ #
106
+ # # Using the block form to completely override how we permit the
107
+ # # parameters for the `sign_up` action.
108
+ # devise_parameter_sanitizer.permit(:sign_up) do |user|
109
+ # user.permit(:email, :password, :password_confirmation)
110
+ # end
111
+ #
112
+ #
113
+ # Returns nothing.
114
+ def permit(action, keys: nil, except: nil, &block)
115
+ if block_given?
116
+ @permitted[action] = block
117
+ end
118
+
119
+ if keys.present?
120
+ @permitted[action] ||= @auth_keys.dup
121
+ @permitted[action].concat(keys)
122
+ end
123
+
124
+ if except.present?
125
+ @permitted[action] ||= @auth_keys.dup
126
+ @permitted[action] = @permitted[action] - except
127
+ end
128
+ end
129
+
130
+ # DEPRECATED: Remove this method on Devise 4.1.
131
+ def for(action, &block) # :nodoc:
132
+ if block_given?
133
+ deprecate_for_with_block(action)
134
+ permit(action, &block)
23
135
  else
24
- default_sanitize(kind)
136
+ deprecate_for_without_block(action)
137
+ @permitted[action] or unknown_action!(action)
25
138
  end
26
139
  end
27
140
 
28
141
  private
29
142
 
30
- def default_for(kind)
31
- raise ArgumentError, "a block is expected in Devise base sanitizer"
32
- end
33
-
34
- def default_sanitize(kind)
35
- default_params
143
+ # Cast a sanitized +ActionController::Parameters+ to a +HashWithIndifferentAccess+
144
+ # that can be used elsewhere.
145
+ #
146
+ # Returns an +ActiveSupport::HashWithIndifferentAccess+.
147
+ def cast_to_hash(params)
148
+ # TODO: Remove the `with_indifferent_access` method call when we only support Rails 5+.
149
+ params && params.to_h.with_indifferent_access
36
150
  end
37
151
 
38
152
  def default_params
39
- params.fetch(resource_name, {})
153
+ @params.fetch(@resource_name, {})
40
154
  end
41
- end
42
155
 
43
- class ParameterSanitizer < BaseSanitizer
44
- def initialize(*)
45
- super
46
- @permitted = Hash.new { |h,k| h[k] = attributes_for(k) }
156
+ def permit_keys(parameters, keys)
157
+ parameters.permit(*keys)
47
158
  end
48
159
 
49
- def sign_in
50
- permit self.for(:sign_in)
51
- end
160
+ def extract_auth_keys(klass)
161
+ auth_keys = klass.authentication_keys
52
162
 
53
- def sign_up
54
- permit self.for(:sign_up)
163
+ auth_keys.respond_to?(:keys) ? auth_keys.keys : auth_keys
55
164
  end
56
165
 
57
- def account_update
58
- permit self.for(:account_update)
166
+ def unknown_action!(action)
167
+ raise NotImplementedError, <<-MESSAGE.strip_heredoc
168
+ "Devise doesn't know how to sanitize parameters for '#{action}'".
169
+ If you want to define a new set of parameters to be sanitized use the
170
+ `permit` method first:
171
+
172
+ devise_parameter_sanitizer.permit(:#{action}, keys: [:param1, :param2, :param3])
173
+ MESSAGE
59
174
  end
60
175
 
61
- private
176
+ def deprecate_for_with_block(action)
177
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
178
+ [Devise] Changing the sanitized parameters through "#{self.class.name}#for(#{action}) is deprecated and it will be removed from Devise 4.1.
179
+ Please use the `permit` method:
62
180
 
63
- # TODO: We do need to flatten so it works with strong_parameters
64
- # gem. We should drop it once we move to Rails 4 only support.
65
- def permit(keys)
66
- default_params.permit(*Array(keys))
181
+ devise_parameter_sanitizer.permit(:#{action}) do |user|
182
+ # Your block here.
183
+ end
184
+ MESSAGE
67
185
  end
68
186
 
69
- # Change for(kind) to return the values in the @permitted
70
- # hash, allowing the developer to customize at runtime.
71
- def default_for(kind)
72
- @permitted[kind] || raise("No sanitizer provided for #{kind}")
73
- end
187
+ def deprecate_for_without_block(action)
188
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
189
+ [Devise] Changing the sanitized parameters through "#{self.class.name}#for(#{action}) is deprecated and it will be removed from Devise 4.1.
190
+ Please use the `permit` method to add or remove any key:
74
191
 
75
- def default_sanitize(kind)
76
- if respond_to?(kind, true)
77
- send(kind)
78
- else
79
- raise NotImplementedError, "Devise doesn't know how to sanitize parameters for #{kind}"
80
- end
81
- end
192
+ To add any new key, use the `keys` keyword argument:
193
+ devise_parameter_sanitizer.permit(:#{action}, keys: [:param1, :param2, :param3])
82
194
 
83
- def attributes_for(kind)
84
- case kind
85
- when :sign_in
86
- auth_keys + [:password, :remember_me]
87
- when :sign_up
88
- auth_keys + [:password, :password_confirmation]
89
- when :account_update
90
- auth_keys + [:password, :password_confirmation, :current_password]
91
- end
195
+ To remove any existing key, use the `except` keyword argument:
196
+ devise_parameter_sanitizer.permit(:#{action}, except: [:email])
197
+ MESSAGE
92
198
  end
93
199
 
94
- def auth_keys
95
- @auth_keys ||= @resource_class.authentication_keys.respond_to?(:keys) ?
96
- @resource_class.authentication_keys.keys : @resource_class.authentication_keys
200
+ def deprecate_instance_method_sanitization(action)
201
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
202
+ [Devise] Parameter sanitization through a "#{self.class.name}##{action}" method is deprecated and it will be removed from Devise 4.1.
203
+ Please use the `permit` method on your sanitizer `initialize` method.
204
+
205
+ class #{self.class.name} < Devise::ParameterSanitizer
206
+ def initialize(*)
207
+ super
208
+ permit(:#{action}, keys: [:param1, :param2, :param3])
209
+ end
210
+ end
211
+ MESSAGE
97
212
  end
98
213
  end
99
214
  end
@@ -1,13 +1,10 @@
1
1
  require "active_support/core_ext/object/try"
2
2
  require "active_support/core_ext/hash/slice"
3
3
 
4
- module ActionDispatch::Routing
5
- class RouteSet #:nodoc:
6
- # Ensure Devise modules are included only after loading routes, because we
7
- # need devise_for mappings already declared to create filters and helpers.
8
- def finalize_with_devise!
9
- result = finalize_without_devise!
10
-
4
+ module Devise
5
+ module RouteSet
6
+ def finalize!
7
+ result = super
11
8
  @devise_finalized ||= begin
12
9
  if Devise.router_name.nil? && defined?(@devise_finalized) && self != Rails.application.try(:routes)
13
10
  warn "[DEVISE] We have detected that you are using devise_for inside engine routes. " \
@@ -21,10 +18,16 @@ module ActionDispatch::Routing
21
18
  Devise.regenerate_helpers!
22
19
  true
23
20
  end
24
-
25
21
  result
26
22
  end
27
- alias_method_chain :finalize!, :devise
23
+ end
24
+ end
25
+
26
+ module ActionDispatch::Routing
27
+ class RouteSet #:nodoc:
28
+ # Ensure Devise modules are included only after loading routes, because we
29
+ # need devise_for mappings already declared to create filters and helpers.
30
+ prepend Devise::RouteSet
28
31
  end
29
32
 
30
33
  class Mapper
@@ -84,20 +87,34 @@ module ActionDispatch::Routing
84
87
  #
85
88
  # You can configure your routes with some options:
86
89
  #
87
- # * class_name: setup a different class to be looked up by devise, if it cannot be
90
+ # * class_name: set up a different class to be looked up by devise, if it cannot be
88
91
  # properly found by the route name.
89
92
  #
90
93
  # devise_for :users, class_name: 'Account'
91
94
  #
92
- # * path: allows you to setup path name that will be used, as rails routes does.
93
- # The following route configuration would setup your route as /accounts instead of /users:
95
+ # * path: allows you to set up path name that will be used, as rails routes does.
96
+ # The following route configuration would set up your route as /accounts instead of /users:
94
97
  #
95
98
  # devise_for :users, path: 'accounts'
96
99
  #
97
- # * singular: setup the singular name for the given resource. This is used as the instance variable
98
- # name in controller, as the name in routes and the scope given to warden.
100
+ # * singular: set up the singular name for the given resource. This is used as the helper methods
101
+ # names in controller ("authenticate_#{singular}!", "#{singular}_signed_in?", "current_#{singular}"
102
+ # and "#{singular}_session"), as the scope name in routes and as the scope given to warden.
99
103
  #
100
- # devise_for :users, singular: :user
104
+ # devise_for :admins, singular: :manager
105
+ #
106
+ # devise_scope :manager do
107
+ # ...
108
+ # end
109
+ #
110
+ # class ManagerController < ApplicationController
111
+ # before_action authenticate_manager!
112
+ #
113
+ # def show
114
+ # @manager = current_manager
115
+ # ...
116
+ # end
117
+ # end
101
118
  #
102
119
  # * path_names: configure different path names to overwrite defaults :sign_in, :sign_out, :sign_up,
103
120
  # :password, :confirmation, :unlock.
@@ -414,27 +431,29 @@ options to another `devise_for` call outside the scope. Here is an example:
414
431
  end
415
432
  ERROR
416
433
  end
417
-
418
- path, @scope[:path] = @scope[:path], nil
434
+ current_scope = @scope.dup
435
+ if @scope.respond_to? :new
436
+ @scope = @scope.new path: nil
437
+ else
438
+ @scope[:path] = nil
439
+ end
419
440
  path_prefix = Devise.omniauth_path_prefix || "/#{mapping.fullpath}/auth".squeeze("/")
420
441
 
421
442
  set_omniauth_path_prefix!(path_prefix)
422
443
 
423
- providers = Regexp.union(mapping.to.omniauth_providers.map(&:to_s))
424
-
425
- match "#{path_prefix}/:provider",
426
- constraints: { provider: providers },
427
- to: "#{controllers[:omniauth_callbacks]}#passthru",
428
- as: :omniauth_authorize,
429
- via: [:get, :post]
444
+ mapping.to.omniauth_providers.each do |provider|
445
+ match "#{path_prefix}/#{provider}",
446
+ to: "#{controllers[:omniauth_callbacks]}#passthru",
447
+ as: "#{provider}_omniauth_authorize",
448
+ via: [:get, :post]
430
449
 
431
- match "#{path_prefix}/:action/callback",
432
- constraints: { action: providers },
433
- to: "#{controllers[:omniauth_callbacks]}#:action",
434
- as: :omniauth_callback,
435
- via: [:get, :post]
450
+ match "#{path_prefix}/#{provider}/callback",
451
+ to: "#{controllers[:omniauth_callbacks]}##{provider}",
452
+ as: "#{provider}_omniauth_callback",
453
+ via: [:get, :post]
454
+ end
436
455
  ensure
437
- @scope[:path] = path
456
+ @scope = current_scope
438
457
  end
439
458
 
440
459
  def with_devise_exclusive_scope(new_path, new_as, options) #:nodoc:
@@ -443,7 +462,11 @@ ERROR
443
462
  exclusive = { as: new_as, path: new_path, module: nil }
444
463
  exclusive.merge!(options.slice(:constraints, :defaults, :options))
445
464
 
446
- exclusive.each_pair { |key, value| @scope[key] = value }
465
+ if @scope.respond_to? :new
466
+ @scope = @scope.new exclusive
467
+ else
468
+ exclusive.each_pair { |key, value| @scope[key] = value }
469
+ end
447
470
  yield
448
471
  ensure
449
472
  @scope = current_scope