devise 3.5.1 → 4.8.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 (257) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +281 -1066
  3. data/MIT-LICENSE +2 -1
  4. data/README.md +292 -97
  5. data/app/controllers/devise/confirmations_controller.rb +3 -1
  6. data/app/controllers/devise/omniauth_callbacks_controller.rb +8 -6
  7. data/app/controllers/devise/passwords_controller.rb +10 -7
  8. data/app/controllers/devise/registrations_controller.rb +39 -18
  9. data/app/controllers/devise/sessions_controller.rb +9 -7
  10. data/app/controllers/devise/unlocks_controller.rb +4 -2
  11. data/app/controllers/devise_controller.rb +25 -12
  12. data/app/helpers/devise_helper.rb +23 -18
  13. data/app/mailers/devise/mailer.rb +13 -3
  14. data/app/views/devise/confirmations/new.html.erb +2 -2
  15. data/app/views/devise/mailer/email_changed.html.erb +7 -0
  16. data/app/views/devise/mailer/password_change.html.erb +3 -0
  17. data/app/views/devise/passwords/edit.html.erb +5 -5
  18. data/app/views/devise/passwords/new.html.erb +2 -2
  19. data/app/views/devise/registrations/edit.html.erb +9 -5
  20. data/app/views/devise/registrations/new.html.erb +4 -4
  21. data/app/views/devise/sessions/new.html.erb +4 -4
  22. data/app/views/devise/shared/_error_messages.html.erb +15 -0
  23. data/app/views/devise/shared/_links.html.erb +8 -8
  24. data/app/views/devise/unlocks/new.html.erb +2 -2
  25. data/config/locales/en.yml +7 -2
  26. data/lib/devise/controllers/helpers.rb +42 -33
  27. data/lib/devise/controllers/rememberable.rb +11 -2
  28. data/lib/devise/controllers/scoped_views.rb +2 -0
  29. data/lib/devise/controllers/sign_in_out.rb +40 -21
  30. data/lib/devise/controllers/store_location.rb +25 -7
  31. data/lib/devise/controllers/url_helpers.rb +3 -1
  32. data/lib/devise/delegator.rb +2 -0
  33. data/lib/devise/encryptor.rb +6 -4
  34. data/lib/devise/failure_app.rb +84 -28
  35. data/lib/devise/hooks/activatable.rb +2 -0
  36. data/lib/devise/hooks/csrf_cleaner.rb +2 -0
  37. data/lib/devise/hooks/forgetable.rb +2 -0
  38. data/lib/devise/hooks/lockable.rb +4 -2
  39. data/lib/devise/hooks/proxy.rb +3 -1
  40. data/lib/devise/hooks/rememberable.rb +2 -0
  41. data/lib/devise/hooks/timeoutable.rb +7 -7
  42. data/lib/devise/hooks/trackable.rb +2 -0
  43. data/lib/devise/mailers/helpers.rb +7 -4
  44. data/lib/devise/mapping.rb +3 -1
  45. data/lib/devise/models/authenticatable.rb +63 -33
  46. data/lib/devise/models/confirmable.rb +108 -35
  47. data/lib/devise/models/database_authenticatable.rb +102 -22
  48. data/lib/devise/models/lockable.rb +24 -6
  49. data/lib/devise/models/omniauthable.rb +2 -0
  50. data/lib/devise/models/recoverable.rb +34 -26
  51. data/lib/devise/models/registerable.rb +4 -0
  52. data/lib/devise/models/rememberable.rb +42 -26
  53. data/lib/devise/models/timeoutable.rb +2 -6
  54. data/lib/devise/models/trackable.rb +15 -1
  55. data/lib/devise/models/validatable.rb +10 -3
  56. data/lib/devise/models.rb +3 -1
  57. data/lib/devise/modules.rb +2 -0
  58. data/lib/devise/omniauth/config.rb +2 -0
  59. data/lib/devise/omniauth/url_helpers.rb +14 -5
  60. data/lib/devise/omniauth.rb +4 -5
  61. data/lib/devise/orm/active_record.rb +5 -1
  62. data/lib/devise/orm/mongoid.rb +6 -2
  63. data/lib/devise/parameter_filter.rb +4 -0
  64. data/lib/devise/parameter_sanitizer.rb +139 -65
  65. data/lib/devise/rails/deprecated_constant_accessor.rb +39 -0
  66. data/lib/devise/rails/routes.rb +71 -51
  67. data/lib/devise/rails/warden_compat.rb +3 -10
  68. data/lib/devise/rails.rb +7 -16
  69. data/lib/devise/secret_key_finder.rb +27 -0
  70. data/lib/devise/strategies/authenticatable.rb +5 -3
  71. data/lib/devise/strategies/base.rb +2 -0
  72. data/lib/devise/strategies/database_authenticatable.rb +11 -4
  73. data/lib/devise/strategies/rememberable.rb +5 -6
  74. data/lib/devise/test/controller_helpers.rb +167 -0
  75. data/lib/devise/test/integration_helpers.rb +63 -0
  76. data/lib/devise/test_helpers.rb +7 -124
  77. data/lib/devise/time_inflector.rb +2 -0
  78. data/lib/devise/token_generator.rb +3 -41
  79. data/lib/devise/version.rb +3 -1
  80. data/lib/devise.rb +69 -46
  81. data/lib/generators/active_record/devise_generator.rb +46 -12
  82. data/lib/generators/active_record/templates/migration.rb +4 -2
  83. data/lib/generators/active_record/templates/migration_existing.rb +4 -2
  84. data/lib/generators/devise/controllers_generator.rb +3 -1
  85. data/lib/generators/devise/devise_generator.rb +5 -3
  86. data/lib/generators/devise/install_generator.rb +18 -5
  87. data/lib/generators/devise/orm_helpers.rb +10 -21
  88. data/lib/generators/devise/views_generator.rb +21 -11
  89. data/lib/generators/mongoid/devise_generator.rb +7 -5
  90. data/lib/generators/templates/README +9 -8
  91. data/lib/generators/templates/controllers/README +1 -1
  92. data/lib/generators/templates/controllers/confirmations_controller.rb +2 -0
  93. data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +3 -1
  94. data/lib/generators/templates/controllers/passwords_controller.rb +2 -0
  95. data/lib/generators/templates/controllers/registrations_controller.rb +6 -4
  96. data/lib/generators/templates/controllers/sessions_controller.rb +4 -2
  97. data/lib/generators/templates/controllers/unlocks_controller.rb +2 -0
  98. data/lib/generators/templates/devise.rb +65 -23
  99. data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
  100. data/lib/generators/templates/markerb/email_changed.markerb +7 -0
  101. data/lib/generators/templates/markerb/password_change.markerb +3 -0
  102. data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
  103. data/lib/generators/templates/markerb/unlock_instructions.markerb +1 -1
  104. data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +5 -1
  105. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +10 -2
  106. data/lib/generators/templates/simple_form_for/passwords/new.html.erb +4 -1
  107. data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +11 -3
  108. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +11 -3
  109. data/lib/generators/templates/simple_form_for/sessions/new.html.erb +7 -2
  110. data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +4 -1
  111. metadata +21 -306
  112. data/.gitignore +0 -10
  113. data/.travis.yml +0 -45
  114. data/.yardopts +0 -9
  115. data/CONTRIBUTING.md +0 -14
  116. data/Gemfile +0 -29
  117. data/Gemfile.lock +0 -191
  118. data/Rakefile +0 -36
  119. data/devise.gemspec +0 -29
  120. data/devise.png +0 -0
  121. data/gemfiles/Gemfile.rails-3.2-stable +0 -29
  122. data/gemfiles/Gemfile.rails-3.2-stable.lock +0 -169
  123. data/gemfiles/Gemfile.rails-4.0-stable +0 -29
  124. data/gemfiles/Gemfile.rails-4.0-stable.lock +0 -163
  125. data/gemfiles/Gemfile.rails-4.1-stable +0 -29
  126. data/gemfiles/Gemfile.rails-4.1-stable.lock +0 -169
  127. data/gemfiles/Gemfile.rails-4.2-stable +0 -29
  128. data/gemfiles/Gemfile.rails-4.2-stable.lock +0 -191
  129. data/script/cached-bundle +0 -49
  130. data/script/s3-put +0 -71
  131. data/test/controllers/custom_registrations_controller_test.rb +0 -40
  132. data/test/controllers/custom_strategy_test.rb +0 -62
  133. data/test/controllers/helpers_test.rb +0 -316
  134. data/test/controllers/inherited_controller_i18n_messages_test.rb +0 -51
  135. data/test/controllers/internal_helpers_test.rb +0 -129
  136. data/test/controllers/load_hooks_controller_test.rb +0 -19
  137. data/test/controllers/passwords_controller_test.rb +0 -31
  138. data/test/controllers/sessions_controller_test.rb +0 -103
  139. data/test/controllers/url_helpers_test.rb +0 -65
  140. data/test/delegator_test.rb +0 -19
  141. data/test/devise_test.rb +0 -107
  142. data/test/failure_app_test.rb +0 -298
  143. data/test/generators/active_record_generator_test.rb +0 -109
  144. data/test/generators/controllers_generator_test.rb +0 -48
  145. data/test/generators/devise_generator_test.rb +0 -39
  146. data/test/generators/install_generator_test.rb +0 -13
  147. data/test/generators/mongoid_generator_test.rb +0 -23
  148. data/test/generators/views_generator_test.rb +0 -96
  149. data/test/helpers/devise_helper_test.rb +0 -49
  150. data/test/integration/authenticatable_test.rb +0 -729
  151. data/test/integration/confirmable_test.rb +0 -324
  152. data/test/integration/database_authenticatable_test.rb +0 -95
  153. data/test/integration/http_authenticatable_test.rb +0 -105
  154. data/test/integration/lockable_test.rb +0 -239
  155. data/test/integration/omniauthable_test.rb +0 -133
  156. data/test/integration/recoverable_test.rb +0 -347
  157. data/test/integration/registerable_test.rb +0 -359
  158. data/test/integration/rememberable_test.rb +0 -176
  159. data/test/integration/timeoutable_test.rb +0 -189
  160. data/test/integration/trackable_test.rb +0 -92
  161. data/test/mailers/confirmation_instructions_test.rb +0 -115
  162. data/test/mailers/reset_password_instructions_test.rb +0 -96
  163. data/test/mailers/unlock_instructions_test.rb +0 -91
  164. data/test/mapping_test.rb +0 -134
  165. data/test/models/authenticatable_test.rb +0 -23
  166. data/test/models/confirmable_test.rb +0 -468
  167. data/test/models/database_authenticatable_test.rb +0 -249
  168. data/test/models/lockable_test.rb +0 -328
  169. data/test/models/omniauthable_test.rb +0 -7
  170. data/test/models/recoverable_test.rb +0 -228
  171. data/test/models/registerable_test.rb +0 -7
  172. data/test/models/rememberable_test.rb +0 -204
  173. data/test/models/serializable_test.rb +0 -49
  174. data/test/models/timeoutable_test.rb +0 -51
  175. data/test/models/trackable_test.rb +0 -41
  176. data/test/models/validatable_test.rb +0 -127
  177. data/test/models_test.rb +0 -144
  178. data/test/omniauth/config_test.rb +0 -57
  179. data/test/omniauth/url_helpers_test.rb +0 -54
  180. data/test/orm/active_record.rb +0 -10
  181. data/test/orm/mongoid.rb +0 -13
  182. data/test/parameter_sanitizer_test.rb +0 -81
  183. data/test/rails_app/Rakefile +0 -6
  184. data/test/rails_app/app/active_record/admin.rb +0 -6
  185. data/test/rails_app/app/active_record/shim.rb +0 -2
  186. data/test/rails_app/app/active_record/user.rb +0 -6
  187. data/test/rails_app/app/active_record/user_on_engine.rb +0 -7
  188. data/test/rails_app/app/active_record/user_on_main_app.rb +0 -7
  189. data/test/rails_app/app/controllers/admins/sessions_controller.rb +0 -6
  190. data/test/rails_app/app/controllers/admins_controller.rb +0 -11
  191. data/test/rails_app/app/controllers/application_controller.rb +0 -12
  192. data/test/rails_app/app/controllers/application_with_fake_engine.rb +0 -30
  193. data/test/rails_app/app/controllers/custom/registrations_controller.rb +0 -31
  194. data/test/rails_app/app/controllers/home_controller.rb +0 -25
  195. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +0 -2
  196. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +0 -2
  197. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +0 -14
  198. data/test/rails_app/app/controllers/users_controller.rb +0 -31
  199. data/test/rails_app/app/helpers/application_helper.rb +0 -3
  200. data/test/rails_app/app/mailers/users/from_proc_mailer.rb +0 -3
  201. data/test/rails_app/app/mailers/users/mailer.rb +0 -3
  202. data/test/rails_app/app/mailers/users/reply_to_mailer.rb +0 -4
  203. data/test/rails_app/app/mongoid/admin.rb +0 -29
  204. data/test/rails_app/app/mongoid/shim.rb +0 -23
  205. data/test/rails_app/app/mongoid/user.rb +0 -39
  206. data/test/rails_app/app/mongoid/user_on_engine.rb +0 -39
  207. data/test/rails_app/app/mongoid/user_on_main_app.rb +0 -39
  208. data/test/rails_app/app/views/admins/index.html.erb +0 -1
  209. data/test/rails_app/app/views/admins/sessions/new.html.erb +0 -2
  210. data/test/rails_app/app/views/home/admin_dashboard.html.erb +0 -1
  211. data/test/rails_app/app/views/home/index.html.erb +0 -1
  212. data/test/rails_app/app/views/home/join.html.erb +0 -1
  213. data/test/rails_app/app/views/home/private.html.erb +0 -1
  214. data/test/rails_app/app/views/home/user_dashboard.html.erb +0 -1
  215. data/test/rails_app/app/views/layouts/application.html.erb +0 -24
  216. data/test/rails_app/app/views/users/edit_form.html.erb +0 -1
  217. data/test/rails_app/app/views/users/index.html.erb +0 -1
  218. data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +0 -1
  219. data/test/rails_app/app/views/users/sessions/new.html.erb +0 -1
  220. data/test/rails_app/bin/bundle +0 -3
  221. data/test/rails_app/bin/rails +0 -4
  222. data/test/rails_app/bin/rake +0 -4
  223. data/test/rails_app/config/application.rb +0 -40
  224. data/test/rails_app/config/boot.rb +0 -14
  225. data/test/rails_app/config/database.yml +0 -18
  226. data/test/rails_app/config/environment.rb +0 -5
  227. data/test/rails_app/config/environments/development.rb +0 -30
  228. data/test/rails_app/config/environments/production.rb +0 -84
  229. data/test/rails_app/config/environments/test.rb +0 -41
  230. data/test/rails_app/config/initializers/backtrace_silencers.rb +0 -7
  231. data/test/rails_app/config/initializers/devise.rb +0 -180
  232. data/test/rails_app/config/initializers/inflections.rb +0 -2
  233. data/test/rails_app/config/initializers/secret_token.rb +0 -8
  234. data/test/rails_app/config/initializers/session_store.rb +0 -1
  235. data/test/rails_app/config/routes.rb +0 -122
  236. data/test/rails_app/config.ru +0 -4
  237. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +0 -71
  238. data/test/rails_app/db/schema.rb +0 -55
  239. data/test/rails_app/lib/shared_admin.rb +0 -17
  240. data/test/rails_app/lib/shared_user.rb +0 -29
  241. data/test/rails_app/lib/shared_user_without_omniauth.rb +0 -13
  242. data/test/rails_app/public/404.html +0 -26
  243. data/test/rails_app/public/422.html +0 -26
  244. data/test/rails_app/public/500.html +0 -26
  245. data/test/rails_app/public/favicon.ico +0 -0
  246. data/test/rails_test.rb +0 -9
  247. data/test/routes_test.rb +0 -264
  248. data/test/support/action_controller/record_identifier.rb +0 -10
  249. data/test/support/assertions.rb +0 -39
  250. data/test/support/helpers.rb +0 -73
  251. data/test/support/integration.rb +0 -92
  252. data/test/support/locale/en.yml +0 -8
  253. data/test/support/mongoid.yml +0 -6
  254. data/test/support/webrat/integrations/rails.rb +0 -24
  255. data/test/test_helper.rb +0 -34
  256. data/test/test_helpers_test.rb +0 -178
  257. data/test/test_models.rb +0 -33
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Models
3
5
  # Registerable is responsible for everything related to registering a new
@@ -19,6 +21,8 @@ module Devise
19
21
  def new_with_session(params, session)
20
22
  new(params)
21
23
  end
24
+
25
+ Devise::Models.config(self, :sign_in_after_change_password)
22
26
  end
23
27
  end
24
28
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'devise/strategies/rememberable'
2
4
  require 'devise/hooks/rememberable'
3
5
  require 'devise/hooks/forgetable'
4
6
 
5
7
  module Devise
6
8
  module Models
7
- # Rememberable manages generating and clearing token for remember the user
9
+ # Rememberable manages generating and clearing token for remembering the user
8
10
  # from a saved cookie. Rememberable also has utility methods for dealing
9
11
  # with serializing the user into the cookie and back from the cookie, trying
10
12
  # to lookup the record based on the saved information.
@@ -39,17 +41,15 @@ module Devise
39
41
  module Rememberable
40
42
  extend ActiveSupport::Concern
41
43
 
42
- attr_accessor :remember_me, :extend_remember_period
44
+ attr_accessor :remember_me
43
45
 
44
46
  def self.required_fields(klass)
45
47
  [:remember_created_at]
46
48
  end
47
49
 
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)
50
+ def remember_me!
51
+ self.remember_token ||= self.class.remember_token if respond_to?(:remember_token)
52
+ self.remember_created_at ||= Time.now.utc
53
53
  save(validate: false) if self.changed?
54
54
  end
55
55
 
@@ -57,19 +57,17 @@ module Devise
57
57
  # it exists), and save the record without validations.
58
58
  def forget_me!
59
59
  return unless persisted?
60
- self.remember_token = nil if respond_to?(:remember_token=)
60
+ self.remember_token = nil if respond_to?(:remember_token)
61
61
  self.remember_created_at = nil if self.class.expire_all_remember_me_on_sign_out
62
62
  save(validate: false)
63
63
  end
64
64
 
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)
65
+ def remember_expires_at
66
+ self.class.remember_for.from_now
68
67
  end
69
68
 
70
- # Remember token expires at created time + remember_for configuration
71
- def remember_expires_at
72
- remember_created_at + self.class.remember_for
69
+ def extend_remember_period
70
+ self.class.extend_remember_period
73
71
  end
74
72
 
75
73
  def rememberable_value
@@ -78,7 +76,7 @@ module Devise
78
76
  elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt.presence)
79
77
  salt
80
78
  else
81
- raise "authenticable_salt returned nil for the #{self.class.name} model. " \
79
+ raise "authenticatable_salt returned nil for the #{self.class.name} model. " \
82
80
  "In order to use rememberable, you must ensure a password is always set " \
83
81
  "or have a remember_token column in your model or implement your own " \
84
82
  "rememberable_value in the model with custom logic."
@@ -102,29 +100,47 @@ module Devise
102
100
  def after_remembered
103
101
  end
104
102
 
105
- protected
103
+ def remember_me?(token, generated_at)
104
+ # TODO: Normalize the JSON type coercion along with the Timeoutable hook
105
+ # in a single place https://github.com/heartcombo/devise/blob/ffe9d6d406e79108cf32a2c6a1d0b3828849c40b/lib/devise/hooks/timeoutable.rb#L14-L18
106
+ if generated_at.is_a?(String)
107
+ generated_at = time_from_json(generated_at)
108
+ end
106
109
 
107
- def generate_remember_token? #:nodoc:
108
- respond_to?(:remember_token) && remember_expired?
110
+ # The token is only valid if:
111
+ # 1. we have a date
112
+ # 2. the current time does not pass the expiry period
113
+ # 3. the record has a remember_created_at date
114
+ # 4. the token date is bigger than the remember_created_at
115
+ # 5. the token matches
116
+ generated_at.is_a?(Time) &&
117
+ (self.class.remember_for.ago < generated_at) &&
118
+ (generated_at > (remember_created_at || Time.now).utc) &&
119
+ Devise.secure_compare(rememberable_value, token)
109
120
  end
110
121
 
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?
122
+ private
123
+
124
+ def time_from_json(value)
125
+ if value =~ /\A\d+\.\d+\Z/
126
+ Time.at(value.to_f)
127
+ else
128
+ Time.parse(value) rescue nil
129
+ end
115
130
  end
116
131
 
117
132
  module ClassMethods
118
133
  # Create the cookie key using the record id and remember_token
119
134
  def serialize_into_cookie(record)
120
- [record.to_key, record.rememberable_value]
135
+ [record.to_key, record.rememberable_value, Time.now.utc.to_f.to_s]
121
136
  end
122
137
 
123
138
  # Recreate the user based on the stored cookie
124
- def serialize_from_cookie(id, remember_token)
139
+ def serialize_from_cookie(*args)
140
+ id, token, generated_at = *args
141
+
125
142
  record = to_adapter.get(id)
126
- record if record && !record.remember_expired? &&
127
- Devise.secure_compare(record.rememberable_value, remember_token)
143
+ record if record && record.remember_me?(token, generated_at)
128
144
  end
129
145
 
130
146
  # Generate a token checking if one does not already exist in the database.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'devise/hooks/timeoutable'
2
4
 
3
5
  module Devise
@@ -26,7 +28,6 @@ module Devise
26
28
 
27
29
  # Checks whether the user session has expired based on configured time.
28
30
  def timedout?(last_access)
29
- return false if remember_exists_and_not_expired?
30
31
  !timeout_in.nil? && last_access && last_access <= timeout_in.ago
31
32
  end
32
33
 
@@ -36,11 +37,6 @@ module Devise
36
37
 
37
38
  private
38
39
 
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
40
  module ClassMethods
45
41
  Devise::Models.config(self, :timeout_in)
46
42
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'devise/hooks/trackable'
2
4
 
3
5
  module Devise
@@ -20,7 +22,7 @@ module Devise
20
22
  self.last_sign_in_at = old_current || new_current
21
23
  self.current_sign_in_at = new_current
22
24
 
23
- old_current, new_current = self.current_sign_in_ip, request.remote_ip
25
+ old_current, new_current = self.current_sign_in_ip, extract_ip_from(request)
24
26
  self.last_sign_in_ip = old_current || new_current
25
27
  self.current_sign_in_ip = new_current
26
28
 
@@ -29,9 +31,21 @@ module Devise
29
31
  end
30
32
 
31
33
  def update_tracked_fields!(request)
34
+ # We have to check if the user is already persisted before running
35
+ # `save` here because invalid users can be saved if we don't.
36
+ # See https://github.com/heartcombo/devise/issues/4673 for more details.
37
+ return if new_record?
38
+
32
39
  update_tracked_fields(request)
33
40
  save(validate: false)
34
41
  end
42
+
43
+ protected
44
+
45
+ def extract_ip_from(request)
46
+ request.remote_ip
47
+ end
48
+
35
49
  end
36
50
  end
37
51
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Models
3
5
  # Validatable creates all needed validations for a user email and password.
@@ -10,7 +12,7 @@ module Devise
10
12
  # Validatable adds the following options to devise_for:
11
13
  #
12
14
  # * +email_regexp+: the regular expression used to validate e-mails;
13
- # * +password_length+: a range expressing password length. Defaults to 8..72.
15
+ # * +password_length+: a range expressing password length. Defaults to 6..128.
14
16
  #
15
17
  module Validatable
16
18
  # All validations used by this module.
@@ -27,8 +29,13 @@ module Devise
27
29
 
28
30
  base.class_eval do
29
31
  validates_presence_of :email, if: :email_required?
30
- validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
31
- validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
32
+ if Devise.activerecord51?
33
+ validates_uniqueness_of :email, allow_blank: true, case_sensitive: true, if: :will_save_change_to_email?
34
+ validates_format_of :email, with: email_regexp, allow_blank: true, if: :will_save_change_to_email?
35
+ else
36
+ validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
37
+ validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
38
+ end
32
39
 
33
40
  validates_presence_of :password, if: :password_required?
34
41
  validates_confirmation_of :password, if: :password_required?
data/lib/devise/models.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Models
3
5
  class MissingAttribute < StandardError
@@ -12,7 +14,7 @@ module Devise
12
14
 
13
15
  # Creates configuration values for Devise and for the given module.
14
16
  #
15
- # Devise::Models.config(Devise::Authenticatable, :stretches, 10)
17
+ # Devise::Models.config(Devise::Models::DatabaseAuthenticatable, :stretches)
16
18
  #
17
19
  # The line above creates:
18
20
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/core_ext/object/with_options'
2
4
 
3
5
  Devise.with_options model: true do |d|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module OmniAuth
3
5
  class StrategyNotFound < NameError
@@ -1,17 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module OmniAuth
3
5
  module UrlHelpers
4
- def self.define_helpers(mapping)
6
+ def omniauth_authorize_path(resource_or_scope, provider, *args)
7
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
8
+ _devise_route_context.send("#{scope}_#{provider}_omniauth_authorize_path", *args)
9
+ end
10
+
11
+ def omniauth_authorize_url(resource_or_scope, provider, *args)
12
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
13
+ _devise_route_context.send("#{scope}_#{provider}_omniauth_authorize_url", *args)
5
14
  end
6
15
 
7
- def omniauth_authorize_path(resource_or_scope, *args)
16
+ def omniauth_callback_path(resource_or_scope, provider, *args)
8
17
  scope = Devise::Mapping.find_scope!(resource_or_scope)
9
- _devise_route_context.send("#{scope}_omniauth_authorize_path", *args)
18
+ _devise_route_context.send("#{scope}_#{provider}_omniauth_callback_path", *args)
10
19
  end
11
20
 
12
- def omniauth_callback_path(resource_or_scope, *args)
21
+ def omniauth_callback_url(resource_or_scope, provider, *args)
13
22
  scope = Devise::Mapping.find_scope!(resource_or_scope)
14
- _devise_route_context.send("#{scope}_omniauth_callback_path", *args)
23
+ _devise_route_context.send("#{scope}_#{provider}_omniauth_callback_url", *args)
15
24
  end
16
25
  end
17
26
  end
@@ -1,15 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
4
+ gem "omniauth", ">= 1.0.0"
5
+
2
6
  require "omniauth"
3
- require "omniauth/version"
4
7
  rescue LoadError
5
8
  warn "Could not load 'omniauth'. Please ensure you have the omniauth gem >= 1.0.0 installed and listed in your Gemfile."
6
9
  raise
7
10
  end
8
11
 
9
- unless OmniAuth::VERSION =~ /^1\./
10
- raise "You are using an old OmniAuth version, please ensure you have 1.0.0.pr2 version or later installed."
11
- end
12
-
13
12
  # Clean up the default path_prefix. It will be automatically set by Devise.
14
13
  OmniAuth.config.path_prefix = nil
15
14
 
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'orm_adapter/adapters/active_record'
2
4
 
3
- ActiveRecord::Base.extend Devise::Models
5
+ ActiveSupport.on_load(:active_record) do
6
+ extend Devise::Models
7
+ end
@@ -1,3 +1,7 @@
1
- require 'orm_adapter/adapters/mongoid'
1
+ # frozen_string_literal: true
2
2
 
3
- Mongoid::Document::ClassMethods.send :include, Devise::Models
3
+ ActiveSupport.on_load(:mongoid) do
4
+ require 'orm_adapter/adapters/mongoid'
5
+
6
+ Mongoid::Document::ClassMethods.send :include, Devise::Models
7
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  class ParameterFilter
3
5
  def initialize(case_insensitive_keys, strip_whitespace_keys)
@@ -16,6 +18,8 @@ module Devise
16
18
 
17
19
  def filtered_hash_by_method_for_given_keys(conditions, method, condition_keys)
18
20
  condition_keys.each do |k|
21
+ next unless conditions.key?(k)
22
+
19
23
  value = conditions[k]
20
24
  conditions[k] = value.send(method) if value.respond_to?(method)
21
25
  end
@@ -1,99 +1,173 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
- class BaseSanitizer
3
- attr_reader :params, :resource_name, :resource_class
4
+ # The +ParameterSanitizer+ deals with permitting specific parameters values
5
+ # for each +Devise+ scope in the application.
6
+ #
7
+ # The sanitizer knows about Devise default parameters (like +password+ and
8
+ # +password_confirmation+ for the `RegistrationsController`), and you can
9
+ # extend or change the permitted parameters list on your controllers.
10
+ #
11
+ # === Permitting new parameters
12
+ #
13
+ # You can add new parameters to the permitted list using the +permit+ method
14
+ # in a +before_action+ method, for instance.
15
+ #
16
+ # class ApplicationController < ActionController::Base
17
+ # before_action :configure_permitted_parameters, if: :devise_controller?
18
+ #
19
+ # protected
20
+ #
21
+ # def configure_permitted_parameters
22
+ # # Permit the `subscribe_newsletter` parameter along with the other
23
+ # # sign up parameters.
24
+ # devise_parameter_sanitizer.permit(:sign_up, keys: [:subscribe_newsletter])
25
+ # end
26
+ # end
27
+ #
28
+ # Using a block yields an +ActionController::Parameters+ object so you can
29
+ # permit nested parameters and have more control over how the parameters are
30
+ # permitted in your controller.
31
+ #
32
+ # def configure_permitted_parameters
33
+ # devise_parameter_sanitizer.permit(:sign_up) do |user|
34
+ # user.permit(newsletter_preferences: [])
35
+ # end
36
+ # end
37
+ class ParameterSanitizer
38
+ DEFAULT_PERMITTED_ATTRIBUTES = {
39
+ sign_in: [:password, :remember_me],
40
+ sign_up: [:password, :password_confirmation],
41
+ account_update: [:password, :password_confirmation, :current_password]
42
+ }
4
43
 
5
44
  def initialize(resource_class, resource_name, params)
6
- @resource_class = resource_class
7
- @resource_name = resource_name
45
+ @auth_keys = extract_auth_keys(resource_class)
8
46
  @params = params
9
- @blocks = Hash.new
10
- end
47
+ @resource_name = resource_name
48
+ @permitted = {}
11
49
 
12
- def for(kind, &block)
13
- if block_given?
14
- @blocks[kind] = block
15
- else
16
- default_for(kind)
50
+ DEFAULT_PERMITTED_ATTRIBUTES.each_pair do |action, keys|
51
+ permit(action, keys: keys)
17
52
  end
18
53
  end
19
54
 
20
- def sanitize(kind)
21
- if block = @blocks[kind]
22
- block.call(default_params)
55
+ # Sanitize the parameters for a specific +action+.
56
+ #
57
+ # === Arguments
58
+ #
59
+ # * +action+ - A +Symbol+ with the action that the controller is
60
+ # performing, like +sign_up+, +sign_in+, etc.
61
+ #
62
+ # === Examples
63
+ #
64
+ # # Inside the `RegistrationsController#create` action.
65
+ # resource = build_resource(devise_parameter_sanitizer.sanitize(:sign_up))
66
+ # resource.save
67
+ #
68
+ # Returns an +ActiveSupport::HashWithIndifferentAccess+ with the permitted
69
+ # attributes.
70
+ def sanitize(action)
71
+ permissions = @permitted[action]
72
+
73
+ if permissions.respond_to?(:call)
74
+ cast_to_hash permissions.call(default_params)
75
+ elsif permissions.present?
76
+ cast_to_hash permit_keys(default_params, permissions)
23
77
  else
24
- default_sanitize(kind)
78
+ unknown_action!(action)
25
79
  end
26
80
  end
27
81
 
28
- private
82
+ # Add or remove new parameters to the permitted list of an +action+.
83
+ #
84
+ # === Arguments
85
+ #
86
+ # * +action+ - A +Symbol+ with the action that the controller is
87
+ # performing, like +sign_up+, +sign_in+, etc.
88
+ # * +keys:+ - An +Array+ of keys that also should be permitted.
89
+ # * +except:+ - An +Array+ of keys that shouldn't be permitted.
90
+ # * +block+ - A block that should be used to permit the action
91
+ # parameters instead of the +Array+ based approach. The block will be
92
+ # called with an +ActionController::Parameters+ instance.
93
+ #
94
+ # === Examples
95
+ #
96
+ # # Adding new parameters to be permitted in the `sign_up` action.
97
+ # devise_parameter_sanitizer.permit(:sign_up, keys: [:subscribe_newsletter])
98
+ #
99
+ # # Removing the `password` parameter from the `account_update` action.
100
+ # devise_parameter_sanitizer.permit(:account_update, except: [:password])
101
+ #
102
+ # # Using the block form to completely override how we permit the
103
+ # # parameters for the `sign_up` action.
104
+ # devise_parameter_sanitizer.permit(:sign_up) do |user|
105
+ # user.permit(:email, :password, :password_confirmation)
106
+ # end
107
+ #
108
+ #
109
+ # Returns nothing.
110
+ def permit(action, keys: nil, except: nil, &block)
111
+ if block_given?
112
+ @permitted[action] = block
113
+ end
29
114
 
30
- def default_for(kind)
31
- raise ArgumentError, "a block is expected in Devise base sanitizer"
32
- end
115
+ if keys.present?
116
+ @permitted[action] ||= @auth_keys.dup
117
+ @permitted[action].concat(keys)
118
+ end
33
119
 
34
- def default_sanitize(kind)
35
- default_params
120
+ if except.present?
121
+ @permitted[action] ||= @auth_keys.dup
122
+ @permitted[action] = @permitted[action] - except
123
+ end
36
124
  end
37
125
 
38
- def default_params
39
- params.fetch(resource_name, {})
40
- end
41
- end
126
+ private
42
127
 
43
- class ParameterSanitizer < BaseSanitizer
44
- def initialize(*)
45
- super
46
- @permitted = Hash.new { |h,k| h[k] = attributes_for(k) }
128
+ # Cast a sanitized +ActionController::Parameters+ to a +HashWithIndifferentAccess+
129
+ # that can be used elsewhere.
130
+ #
131
+ # Returns an +ActiveSupport::HashWithIndifferentAccess+.
132
+ def cast_to_hash(params)
133
+ # TODO: Remove the `with_indifferent_access` method call when we only support Rails 5+.
134
+ params && params.to_h.with_indifferent_access
47
135
  end
48
136
 
49
- def sign_in
50
- permit self.for(:sign_in)
137
+ def default_params
138
+ if hashable_resource_params?
139
+ @params.fetch(@resource_name)
140
+ else
141
+ empty_params
142
+ end
51
143
  end
52
144
 
53
- def sign_up
54
- permit self.for(:sign_up)
145
+ def hashable_resource_params?
146
+ @params[@resource_name].respond_to?(:permit)
55
147
  end
56
148
 
57
- def account_update
58
- permit self.for(:account_update)
149
+ def empty_params
150
+ ActionController::Parameters.new({})
59
151
  end
60
152
 
61
- private
62
-
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))
153
+ def permit_keys(parameters, keys)
154
+ parameters.permit(*keys)
67
155
  end
68
156
 
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
157
+ def extract_auth_keys(klass)
158
+ auth_keys = klass.authentication_keys
74
159
 
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
160
+ auth_keys.respond_to?(:keys) ? auth_keys.keys : auth_keys
81
161
  end
82
162
 
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
92
- end
163
+ def unknown_action!(action)
164
+ raise NotImplementedError, <<-MESSAGE.strip_heredoc
165
+ "Devise doesn't know how to sanitize parameters for '#{action}'".
166
+ If you want to define a new set of parameters to be sanitized use the
167
+ `permit` method first:
93
168
 
94
- def auth_keys
95
- @auth_keys ||= @resource_class.authentication_keys.respond_to?(:keys) ?
96
- @resource_class.authentication_keys.keys : @resource_class.authentication_keys
169
+ devise_parameter_sanitizer.permit(:#{action}, keys: [:param1, :param2, :param3])
170
+ MESSAGE
97
171
  end
98
172
  end
99
173
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'active_support/deprecation/constant_accessor'
5
+
6
+ module Devise
7
+ DeprecatedConstantAccessor = ActiveSupport::Deprecation::DeprecatedConstantAccessor #:nodoc:
8
+ end
9
+ rescue LoadError
10
+
11
+ # Copy of constant deprecation module from Rails / Active Support version 6, so we can use it
12
+ # with Rails <= 5.0 versions. This can be removed once we support only Rails 5.1 or greater.
13
+ module Devise
14
+ module DeprecatedConstantAccessor #:nodoc:
15
+ def self.included(base)
16
+ require "active_support/inflector/methods"
17
+
18
+ extension = Module.new do
19
+ def const_missing(missing_const_name)
20
+ if class_variable_defined?(:@@_deprecated_constants)
21
+ if (replacement = class_variable_get(:@@_deprecated_constants)[missing_const_name.to_s])
22
+ replacement[:deprecator].warn(replacement[:message] || "#{name}::#{missing_const_name} is deprecated! Use #{replacement[:new]} instead.", Rails::VERSION::MAJOR == 4 ? caller : caller_locations)
23
+ return ActiveSupport::Inflector.constantize(replacement[:new].to_s)
24
+ end
25
+ end
26
+ super
27
+ end
28
+
29
+ def deprecate_constant(const_name, new_constant, message: nil, deprecator: ActiveSupport::Deprecation.instance)
30
+ class_variable_set(:@@_deprecated_constants, {}) unless class_variable_defined?(:@@_deprecated_constants)
31
+ class_variable_get(:@@_deprecated_constants)[const_name.to_s] = { new: new_constant, message: message, deprecator: deprecator }
32
+ end
33
+ end
34
+ base.singleton_class.prepend extension
35
+ end
36
+ end
37
+ end
38
+
39
+ end