devise 3.2.4 → 4.0.0

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.

Files changed (178) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.travis.yml +33 -17
  4. data/CHANGELOG.md +57 -1033
  5. data/CODE_OF_CONDUCT.md +22 -0
  6. data/CONTRIBUTING.md +2 -0
  7. data/Gemfile +5 -5
  8. data/Gemfile.lock +138 -115
  9. data/MIT-LICENSE +1 -1
  10. data/README.md +124 -65
  11. data/Rakefile +2 -1
  12. data/app/controllers/devise/confirmations_controller.rb +7 -3
  13. data/app/controllers/devise/omniauth_callbacks_controller.rb +8 -4
  14. data/app/controllers/devise/passwords_controller.rb +16 -6
  15. data/app/controllers/devise/registrations_controller.rb +22 -10
  16. data/app/controllers/devise/sessions_controller.rb +42 -14
  17. data/app/controllers/devise/unlocks_controller.rb +5 -2
  18. data/app/controllers/devise_controller.rb +63 -29
  19. data/app/mailers/devise/mailer.rb +4 -0
  20. data/app/views/devise/confirmations/new.html.erb +7 -3
  21. data/app/views/devise/mailer/password_change.html.erb +3 -0
  22. data/app/views/devise/passwords/edit.html.erb +14 -5
  23. data/app/views/devise/passwords/new.html.erb +7 -3
  24. data/app/views/devise/registrations/edit.html.erb +19 -9
  25. data/app/views/devise/registrations/new.html.erb +18 -7
  26. data/app/views/devise/sessions/new.html.erb +16 -7
  27. data/app/views/devise/shared/{_links.erb → _links.html.erb} +2 -2
  28. data/app/views/devise/unlocks/new.html.erb +7 -3
  29. data/bin/test +13 -0
  30. data/config/locales/en.yml +19 -16
  31. data/devise.gemspec +3 -4
  32. data/gemfiles/{Gemfile.rails-3.2-stable → Gemfile.rails-4.1-stable} +6 -6
  33. data/gemfiles/Gemfile.rails-4.1-stable.lock +167 -0
  34. data/gemfiles/{Gemfile.rails-head → Gemfile.rails-4.2-stable} +6 -6
  35. data/gemfiles/Gemfile.rails-4.2-stable.lock +189 -0
  36. data/gemfiles/Gemfile.rails-5.0-beta +37 -0
  37. data/gemfiles/Gemfile.rails-5.0-beta.lock +199 -0
  38. data/lib/devise/controllers/helpers.rb +94 -27
  39. data/lib/devise/controllers/rememberable.rb +9 -2
  40. data/lib/devise/controllers/sign_in_out.rb +2 -9
  41. data/lib/devise/controllers/store_location.rb +11 -3
  42. data/lib/devise/controllers/url_helpers.rb +7 -7
  43. data/lib/devise/encryptor.rb +22 -0
  44. data/lib/devise/failure_app.rb +72 -23
  45. data/lib/devise/hooks/activatable.rb +3 -4
  46. data/lib/devise/hooks/csrf_cleaner.rb +3 -1
  47. data/lib/devise/hooks/timeoutable.rb +13 -8
  48. data/lib/devise/mailers/helpers.rb +1 -1
  49. data/lib/devise/mapping.rb +6 -2
  50. data/lib/devise/models/authenticatable.rb +32 -28
  51. data/lib/devise/models/confirmable.rb +55 -22
  52. data/lib/devise/models/database_authenticatable.rb +32 -19
  53. data/lib/devise/models/lockable.rb +5 -5
  54. data/lib/devise/models/recoverable.rb +44 -20
  55. data/lib/devise/models/rememberable.rb +54 -27
  56. data/lib/devise/models/timeoutable.rb +0 -6
  57. data/lib/devise/models/trackable.rb +5 -3
  58. data/lib/devise/models/validatable.rb +3 -3
  59. data/lib/devise/models.rb +1 -1
  60. data/lib/devise/omniauth/url_helpers.rb +62 -4
  61. data/lib/devise/parameter_sanitizer.rb +176 -61
  62. data/lib/devise/rails/routes.rb +76 -59
  63. data/lib/devise/rails/warden_compat.rb +1 -10
  64. data/lib/devise/rails.rb +2 -11
  65. data/lib/devise/strategies/authenticatable.rb +15 -6
  66. data/lib/devise/strategies/database_authenticatable.rb +5 -4
  67. data/lib/devise/strategies/rememberable.rb +13 -3
  68. data/lib/devise/test_helpers.rb +12 -7
  69. data/lib/devise/token_generator.rb +1 -41
  70. data/lib/devise/version.rb +1 -1
  71. data/lib/devise.rb +150 -58
  72. data/lib/generators/active_record/devise_generator.rb +28 -4
  73. data/lib/generators/active_record/templates/migration.rb +3 -3
  74. data/lib/generators/active_record/templates/migration_existing.rb +3 -3
  75. data/lib/generators/devise/controllers_generator.rb +44 -0
  76. data/lib/generators/devise/install_generator.rb +15 -0
  77. data/lib/generators/devise/orm_helpers.rb +1 -18
  78. data/lib/generators/devise/views_generator.rb +14 -3
  79. data/lib/generators/templates/README +1 -1
  80. data/lib/generators/templates/controllers/README +14 -0
  81. data/lib/generators/templates/controllers/confirmations_controller.rb +28 -0
  82. data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +28 -0
  83. data/lib/generators/templates/controllers/passwords_controller.rb +32 -0
  84. data/lib/generators/templates/controllers/registrations_controller.rb +60 -0
  85. data/lib/generators/templates/controllers/sessions_controller.rb +25 -0
  86. data/lib/generators/templates/controllers/unlocks_controller.rb +28 -0
  87. data/lib/generators/templates/devise.rb +36 -28
  88. data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
  89. data/lib/generators/templates/markerb/password_change.markerb +3 -0
  90. data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
  91. data/lib/generators/templates/markerb/unlock_instructions.markerb +1 -1
  92. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +1 -1
  93. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +1 -1
  94. data/lib/generators/templates/simple_form_for/sessions/new.html.erb +2 -2
  95. data/test/controllers/custom_registrations_controller_test.rb +40 -0
  96. data/test/controllers/custom_strategy_test.rb +7 -5
  97. data/test/controllers/helper_methods_test.rb +22 -0
  98. data/test/controllers/helpers_test.rb +41 -1
  99. data/test/controllers/inherited_controller_i18n_messages_test.rb +51 -0
  100. data/test/controllers/internal_helpers_test.rb +19 -15
  101. data/test/controllers/load_hooks_controller_test.rb +19 -0
  102. data/test/controllers/passwords_controller_test.rb +5 -4
  103. data/test/controllers/sessions_controller_test.rb +24 -21
  104. data/test/controllers/url_helpers_test.rb +7 -1
  105. data/test/devise_test.rb +48 -8
  106. data/test/failure_app_test.rb +107 -19
  107. data/test/generators/active_record_generator_test.rb +6 -26
  108. data/test/generators/controllers_generator_test.rb +48 -0
  109. data/test/generators/install_generator_test.rb +14 -3
  110. data/test/generators/views_generator_test.rb +8 -1
  111. data/test/helpers/devise_helper_test.rb +10 -12
  112. data/test/integration/authenticatable_test.rb +37 -21
  113. data/test/integration/confirmable_test.rb +54 -14
  114. data/test/integration/database_authenticatable_test.rb +12 -1
  115. data/test/integration/http_authenticatable_test.rb +4 -5
  116. data/test/integration/lockable_test.rb +10 -9
  117. data/test/integration/omniauthable_test.rb +13 -11
  118. data/test/integration/recoverable_test.rb +28 -15
  119. data/test/integration/registerable_test.rb +41 -33
  120. data/test/integration/rememberable_test.rb +51 -7
  121. data/test/integration/timeoutable_test.rb +23 -22
  122. data/test/integration/trackable_test.rb +3 -3
  123. data/test/mailers/confirmation_instructions_test.rb +10 -10
  124. data/test/mailers/reset_password_instructions_test.rb +8 -8
  125. data/test/mailers/unlock_instructions_test.rb +8 -8
  126. data/test/mapping_test.rb +7 -0
  127. data/test/models/authenticatable_test.rb +11 -1
  128. data/test/models/confirmable_test.rb +91 -42
  129. data/test/models/database_authenticatable_test.rb +26 -6
  130. data/test/models/lockable_test.rb +29 -17
  131. data/test/models/recoverable_test.rb +74 -7
  132. data/test/models/rememberable_test.rb +68 -94
  133. data/test/models/trackable_test.rb +28 -0
  134. data/test/models/validatable_test.rb +9 -17
  135. data/test/models_test.rb +15 -6
  136. data/test/omniauth/url_helpers_test.rb +4 -7
  137. data/test/orm/active_record.rb +6 -1
  138. data/test/parameter_sanitizer_test.rb +103 -53
  139. data/test/rails_app/app/active_record/user.rb +1 -0
  140. data/test/rails_app/app/active_record/user_on_engine.rb +7 -0
  141. data/test/rails_app/app/active_record/user_on_main_app.rb +7 -0
  142. data/test/rails_app/app/active_record/user_without_email.rb +8 -0
  143. data/test/rails_app/app/controllers/admins_controller.rb +1 -6
  144. data/test/rails_app/app/controllers/application_controller.rb +5 -2
  145. data/test/rails_app/app/controllers/application_with_fake_engine.rb +30 -0
  146. data/test/rails_app/app/controllers/custom/registrations_controller.rb +31 -0
  147. data/test/rails_app/app/controllers/home_controller.rb +5 -1
  148. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +3 -3
  149. data/test/rails_app/app/controllers/users_controller.rb +6 -6
  150. data/test/rails_app/app/mailers/users/from_proc_mailer.rb +3 -0
  151. data/test/rails_app/app/mailers/users/mailer.rb +0 -9
  152. data/test/rails_app/app/mailers/users/reply_to_mailer.rb +4 -0
  153. data/test/rails_app/app/mongoid/user_on_engine.rb +39 -0
  154. data/test/rails_app/app/mongoid/user_on_main_app.rb +39 -0
  155. data/test/rails_app/app/mongoid/user_without_email.rb +33 -0
  156. data/test/rails_app/config/application.rb +3 -3
  157. data/test/rails_app/config/boot.rb +4 -4
  158. data/test/rails_app/config/environments/production.rb +6 -2
  159. data/test/rails_app/config/environments/test.rb +13 -3
  160. data/test/rails_app/config/initializers/devise.rb +15 -16
  161. data/test/rails_app/config/initializers/secret_token.rb +1 -6
  162. data/test/rails_app/config/routes.rb +23 -3
  163. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +2 -2
  164. data/test/rails_app/lib/shared_user.rb +1 -1
  165. data/test/rails_app/lib/shared_user_without_email.rb +26 -0
  166. data/test/rails_app/lib/shared_user_without_omniauth.rb +13 -0
  167. data/test/rails_test.rb +9 -0
  168. data/test/routes_test.rb +33 -16
  169. data/test/support/assertions.rb +2 -3
  170. data/test/support/helpers.rb +13 -6
  171. data/test/support/http_method_compatibility.rb +51 -0
  172. data/test/support/integration.rb +4 -4
  173. data/test/support/webrat/integrations/rails.rb +9 -0
  174. data/test/test_helper.rb +7 -0
  175. data/test/test_helpers_test.rb +43 -38
  176. data/test/test_models.rb +3 -3
  177. metadata +77 -23
  178. data/gemfiles/Gemfile.rails-4.0-stable +0 -29
@@ -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.
103
+ #
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!
99
112
  #
100
- # devise_for :users, singular: :user
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.
@@ -119,7 +136,7 @@ module ActionDispatch::Routing
119
136
  # * sign_out_via: the HTTP method(s) accepted for the :sign_out action (default: :get),
120
137
  # if you wish to restrict this to accept only :post or :delete requests you should do:
121
138
  #
122
- # devise_for :users, sign_out_via: [ :post, :delete ]
139
+ # devise_for :users, sign_out_via: [:post, :delete]
123
140
  #
124
141
  # You need to make sure that your sign_out controls trigger a request with a matching HTTP method.
125
142
  #
@@ -129,7 +146,8 @@ module ActionDispatch::Routing
129
146
  #
130
147
  # devise_for :users, module: "users"
131
148
  #
132
- # * skip: tell which controller you want to skip routes from being created:
149
+ # * skip: tell which controller you want to skip routes from being created.
150
+ # It accepts :all as an option, meaning it will not generate any route at all:
133
151
  #
134
152
  # devise_for :users, skip: :sessions
135
153
  #
@@ -153,6 +171,8 @@ module ActionDispatch::Routing
153
171
  #
154
172
  # * defaults: works the same as Rails' defaults
155
173
  #
174
+ # * router_name: allows application level router name to be overwritten for the current scope
175
+ #
156
176
  # ==== Scoping
157
177
  #
158
178
  # Following Rails 3 routes DSL, you can nest devise_for calls inside a scope:
@@ -224,7 +244,7 @@ module ActionDispatch::Routing
224
244
  raise_no_devise_method_error!(mapping.class_name) unless mapping.to.respond_to?(:devise)
225
245
  rescue NameError => e
226
246
  raise unless mapping.class_name == resource.to_s.classify
227
- warn "[WARNING] You provided devise_for #{resource.inspect} but there is " <<
247
+ warn "[WARNING] You provided devise_for #{resource.inspect} but there is " \
228
248
  "no model #{mapping.class_name} defined in your application"
229
249
  next
230
250
  rescue NoMethodError => e
@@ -234,13 +254,12 @@ module ActionDispatch::Routing
234
254
 
235
255
  if options[:controllers] && options[:controllers][:omniauth_callbacks]
236
256
  unless mapping.omniauthable?
237
- msg = "Mapping omniauth_callbacks on a resource that is not omniauthable\n"
238
- msg << "Please add `devise :omniauthable` to the `#{mapping.class_name}` model"
239
- raise msg
257
+ raise ArgumentError, "Mapping omniauth_callbacks on a resource that is not omniauthable\n" \
258
+ "Please add `devise :omniauthable` to the `#{mapping.class_name}` model"
240
259
  end
241
260
  end
242
261
 
243
- routes = mapping.used_routes
262
+ routes = mapping.used_routes
244
263
 
245
264
  devise_scope mapping.name do
246
265
  with_devise_exclusive_scope mapping.fullpath, mapping.name, options do
@@ -400,59 +419,57 @@ module ActionDispatch::Routing
400
419
  def devise_omniauth_callback(mapping, controllers) #:nodoc:
401
420
  if mapping.fullpath =~ /:[a-zA-Z_]/
402
421
  raise <<-ERROR
403
- Devise does not support scoping omniauth callbacks under a dynamic segment
422
+ Devise does not support scoping OmniAuth callbacks under a dynamic segment
404
423
  and you have set #{mapping.fullpath.inspect}. You can work around by passing
405
- `skip: :omniauth_callbacks` and manually defining the routes. Here is an example:
406
-
407
- match "/users/auth/:provider",
408
- constraints: { provider: /google|facebook/ },
409
- to: "devise/omniauth_callbacks#passthru",
410
- as: :omniauth_authorize,
411
- via: [:get, :post]
412
-
413
- match "/users/auth/:action/callback",
414
- constraints: { action: /google|facebook/ },
415
- to: "devise/omniauth_callbacks",
416
- as: :omniauth_callback,
417
- via: [:get, :post]
424
+ `skip: :omniauth_callbacks` to the `devise_for` call and extract omniauth
425
+ options to another `devise_for` call outside the scope. Here is an example:
426
+
427
+ devise_for :users, only: :omniauth_callbacks, controllers: {omniauth_callbacks: 'users/omniauth_callbacks'}
428
+
429
+ scope '/(:locale)', locale: /ru|en/ do
430
+ devise_for :users, skip: :omniauth_callbacks
431
+ end
418
432
  ERROR
419
433
  end
420
-
421
- 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
422
440
  path_prefix = Devise.omniauth_path_prefix || "/#{mapping.fullpath}/auth".squeeze("/")
423
441
 
424
442
  set_omniauth_path_prefix!(path_prefix)
425
443
 
426
- providers = Regexp.union(mapping.to.omniauth_providers.map(&:to_s))
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]
427
449
 
428
- match "#{path_prefix}/:provider",
429
- constraints: { provider: providers },
430
- to: "#{controllers[:omniauth_callbacks]}#passthru",
431
- as: :omniauth_authorize,
432
- via: [:get, :post]
433
-
434
- match "#{path_prefix}/:action/callback",
435
- constraints: { action: providers },
436
- to: controllers[:omniauth_callbacks],
437
- as: :omniauth_callback,
438
- 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
439
455
  ensure
440
- @scope[:path] = path
456
+ @scope = current_scope
441
457
  end
442
458
 
443
- DEVISE_SCOPE_KEYS = [:as, :path, :module, :constraints, :defaults, :options]
444
-
445
459
  def with_devise_exclusive_scope(new_path, new_as, options) #:nodoc:
446
- old = {}
447
- DEVISE_SCOPE_KEYS.each { |k| old[k] = @scope[k] }
460
+ current_scope = @scope.dup
448
461
 
449
- new = { as: new_as, path: new_path, module: nil }
450
- new.merge!(options.slice(:constraints, :defaults, :options))
462
+ exclusive = { as: new_as, path: new_path, module: nil }
463
+ exclusive.merge!(options.slice(:constraints, :defaults, :options))
451
464
 
452
- @scope.merge!(new)
465
+ if @scope.respond_to? :new
466
+ @scope = @scope.new exclusive
467
+ else
468
+ exclusive.each_pair { |key, value| @scope[key] = value }
469
+ end
453
470
  yield
454
471
  ensure
455
- @scope.merge!(old)
472
+ @scope = current_scope
456
473
  end
457
474
 
458
475
  def constraints_for(method_to_apply, scope=nil, block=nil)
@@ -3,17 +3,8 @@ module Warden::Mixins::Common
3
3
  @request ||= ActionDispatch::Request.new(env)
4
4
  end
5
5
 
6
- # Deprecate: Remove this check once we move to Rails 4 only.
7
- NULL_STORE =
8
- defined?(ActionController::RequestForgeryProtection::ProtectionMethods::NullSession::NullSessionHash) ?
9
- ActionController::RequestForgeryProtection::ProtectionMethods::NullSession::NullSessionHash : nil
10
-
11
6
  def reset_session!
12
- # Calling reset_session on NULL_STORE causes it fail.
13
- # This is a bug that needs to be fixed in Rails.
14
- unless NULL_STORE && request.session.is_a?(NULL_STORE)
15
- request.reset_session
16
- end
7
+ request.reset_session
17
8
  end
18
9
 
19
10
  def cookies
data/lib/devise/rails.rb CHANGED
@@ -17,7 +17,7 @@ module Devise
17
17
  Devise.include_helpers(Devise::Controllers)
18
18
  end
19
19
 
20
- initializer "devise.omniauth" do |app|
20
+ initializer "devise.omniauth", after: :load_config_initializers, before: :build_middleware_stack do |app|
21
21
  Devise.omniauth_configs.each do |provider, config|
22
22
  app.middleware.use config.strategy_class, *config.args do |strategy|
23
23
  config.strategy = strategy
@@ -39,18 +39,9 @@ module Devise
39
39
  Devise.token_generator ||=
40
40
  if secret_key = Devise.secret_key
41
41
  Devise::TokenGenerator.new(
42
- Devise::CachingKeyGenerator.new(Devise::KeyGenerator.new(secret_key))
42
+ ActiveSupport::CachingKeyGenerator.new(ActiveSupport::KeyGenerator.new(secret_key))
43
43
  )
44
44
  end
45
45
  end
46
-
47
- initializer "devise.fix_routes_proxy_missing_respond_to_bug" do
48
- # Deprecate: Remove once we move to Rails 4 only.
49
- ActionDispatch::Routing::RoutesProxy.class_eval do
50
- def respond_to?(method, include_private = false)
51
- super || routes.url_helpers.respond_to?(method)
52
- end
53
- end
54
- end
55
46
  end
56
47
  end
@@ -16,11 +16,18 @@ module Devise
16
16
  valid_for_params_auth? || valid_for_http_auth?
17
17
  end
18
18
 
19
+ # Override and set to false for things like OmniAuth that technically
20
+ # run through Authentication (user_set) very often, which would normally
21
+ # reset CSRF data in the session
22
+ def clean_up_csrf?
23
+ true
24
+ end
25
+
19
26
  private
20
27
 
21
28
  # Receives a resource and check if it is valid by calling valid_for_authentication?
22
29
  # An optional block that will be triggered while validating can be optionally
23
- # given as parameter. Check Devise::Models::Authenticable.valid_for_authentication?
30
+ # given as parameter. Check Devise::Models::Authenticatable.valid_for_authentication?
24
31
  # for more information.
25
32
  #
26
33
  # In case the resource can't be validated, it will fail with the given
@@ -29,7 +36,6 @@ module Devise
29
36
  result = resource && resource.valid_for_authentication?(&block)
30
37
 
31
38
  if result
32
- decorate(resource)
33
39
  true
34
40
  else
35
41
  if resource
@@ -40,7 +46,7 @@ module Devise
40
46
  end
41
47
 
42
48
  # Get values from params and set in the resource.
43
- def decorate(resource)
49
+ def remember_me(resource)
44
50
  resource.remember_me = remember_me? if resource.respond_to?(:remember_me=)
45
51
  end
46
52
 
@@ -51,7 +57,7 @@ module Devise
51
57
 
52
58
  # Check if this is a valid strategy for http authentication by:
53
59
  #
54
- # * Validating if the model allows params authentication;
60
+ # * Validating if the model allows http authentication;
55
61
  # * If any of the authorization headers were sent;
56
62
  # * If all authentication keys are present;
57
63
  #
@@ -102,14 +108,17 @@ module Devise
102
108
  params_auth_hash.is_a?(Hash)
103
109
  end
104
110
 
105
- # Check if password is present.
111
+ # Note: unlike `Model.valid_password?`, this method does not actually
112
+ # ensure that the password in the params matches the password stored in
113
+ # the database. It only checks if the password is *present*. Do not rely
114
+ # on this method for validating that a given password is correct.
106
115
  def valid_password?
107
116
  password.present?
108
117
  end
109
118
 
110
119
  # Helper to decode credentials from HTTP.
111
120
  def decode_credentials
112
- return [] unless request.authorization && request.authorization =~ /^Basic (.*)/m
121
+ return [] unless request.authorization && request.authorization =~ /^Basic (.*)/mi
113
122
  Base64.decode64($1).split(/:/, 2)
114
123
  end
115
124
 
@@ -5,15 +5,16 @@ module Devise
5
5
  # Default strategy for signing in a user, based on their email and password in the database.
6
6
  class DatabaseAuthenticatable < Authenticatable
7
7
  def authenticate!
8
- resource = valid_password? && mapping.to.find_for_database_authentication(authentication_hash)
9
- encrypted = false
8
+ resource = password.present? && mapping.to.find_for_database_authentication(authentication_hash)
9
+ hashed = false
10
10
 
11
- if validate(resource){ encrypted = true; resource.valid_password?(password) }
11
+ if validate(resource){ hashed = true; resource.valid_password?(password) }
12
+ remember_me(resource)
12
13
  resource.after_database_authentication
13
14
  success!(resource)
14
15
  end
15
16
 
16
- mapping.to.new.password = password if !encrypted && Devise.paranoid
17
+ mapping.to.new.password = password if !hashed && Devise.paranoid
17
18
  fail(:not_found_in_database) unless resource
18
19
  end
19
20
  end
@@ -25,15 +25,25 @@ module Devise
25
25
  end
26
26
 
27
27
  if validate(resource)
28
+ remember_me(resource) if extend_remember_me?(resource)
29
+ resource.after_remembered
28
30
  success!(resource)
29
31
  end
30
32
  end
31
33
 
34
+ # No need to clean up the CSRF when using rememberable.
35
+ # In fact, cleaning it up here would be a bug because
36
+ # rememberable is triggered on GET requests which means
37
+ # we would render a page on first access with all csrf
38
+ # tokens expired.
39
+ def clean_up_csrf?
40
+ false
41
+ end
42
+
32
43
  private
33
44
 
34
- def decorate(resource)
35
- super
36
- resource.extend_remember_period = mapping.to.extend_remember_period if resource.respond_to?(:extend_remember_period=)
45
+ def extend_remember_me?(resource)
46
+ resource.respond_to?(:extend_remember_period) && resource.extend_remember_period
37
47
  end
38
48
 
39
49
  def remember_me?