devise 3.2.1 → 4.4.3

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 (254) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +58 -10
  3. data/CHANGELOG.md +199 -979
  4. data/CODE_OF_CONDUCT.md +22 -0
  5. data/CONTRIBUTING.md +73 -8
  6. data/Gemfile +19 -11
  7. data/Gemfile.lock +152 -119
  8. data/ISSUE_TEMPLATE.md +19 -0
  9. data/MIT-LICENSE +1 -1
  10. data/README.md +347 -93
  11. data/Rakefile +4 -2
  12. data/app/controllers/devise/confirmations_controller.rb +11 -5
  13. data/app/controllers/devise/omniauth_callbacks_controller.rb +12 -6
  14. data/app/controllers/devise/passwords_controller.rb +20 -8
  15. data/app/controllers/devise/registrations_controller.rb +34 -19
  16. data/app/controllers/devise/sessions_controller.rb +47 -17
  17. data/app/controllers/devise/unlocks_controller.rb +9 -4
  18. data/app/controllers/devise_controller.rb +67 -31
  19. data/app/helpers/devise_helper.rb +4 -2
  20. data/app/mailers/devise/mailer.rb +10 -0
  21. data/app/views/devise/confirmations/new.html.erb +8 -4
  22. data/app/views/devise/mailer/confirmation_instructions.html.erb +1 -1
  23. data/app/views/devise/mailer/email_changed.html.erb +7 -0
  24. data/app/views/devise/mailer/password_change.html.erb +3 -0
  25. data/app/views/devise/mailer/reset_password_instructions.html.erb +1 -1
  26. data/app/views/devise/mailer/unlock_instructions.html.erb +1 -1
  27. data/app/views/devise/passwords/edit.html.erb +15 -6
  28. data/app/views/devise/passwords/new.html.erb +8 -4
  29. data/app/views/devise/registrations/edit.html.erb +28 -14
  30. data/app/views/devise/registrations/new.html.erb +19 -8
  31. data/app/views/devise/sessions/new.html.erb +17 -8
  32. data/app/views/devise/shared/{_links.erb → _links.html.erb} +2 -2
  33. data/app/views/devise/unlocks/new.html.erb +8 -4
  34. data/bin/test +13 -0
  35. data/config/locales/en.yml +22 -17
  36. data/devise.gemspec +7 -6
  37. data/gemfiles/Gemfile.rails-4.1-stable +32 -0
  38. data/gemfiles/Gemfile.rails-4.1-stable.lock +171 -0
  39. data/gemfiles/Gemfile.rails-4.2-stable +32 -0
  40. data/gemfiles/Gemfile.rails-4.2-stable.lock +192 -0
  41. data/gemfiles/Gemfile.rails-5.0-stable +33 -0
  42. data/gemfiles/Gemfile.rails-5.0-stable.lock +192 -0
  43. data/gemfiles/Gemfile.rails-5.2-rc1 +26 -0
  44. data/gemfiles/Gemfile.rails-5.2-rc1.lock +201 -0
  45. data/guides/bug_report_templates/integration_test.rb +106 -0
  46. data/lib/devise.rb +107 -84
  47. data/lib/devise/controllers/helpers.rb +111 -31
  48. data/lib/devise/controllers/rememberable.rb +15 -6
  49. data/lib/devise/controllers/scoped_views.rb +3 -1
  50. data/lib/devise/controllers/sign_in_out.rb +39 -26
  51. data/lib/devise/controllers/store_location.rb +31 -2
  52. data/lib/devise/controllers/url_helpers.rb +9 -7
  53. data/lib/devise/delegator.rb +2 -0
  54. data/lib/devise/encryptor.rb +24 -0
  55. data/lib/devise/failure_app.rb +98 -39
  56. data/lib/devise/hooks/activatable.rb +7 -6
  57. data/lib/devise/hooks/csrf_cleaner.rb +5 -1
  58. data/lib/devise/hooks/forgetable.rb +2 -0
  59. data/lib/devise/hooks/lockable.rb +7 -2
  60. data/lib/devise/hooks/proxy.rb +4 -2
  61. data/lib/devise/hooks/rememberable.rb +4 -2
  62. data/lib/devise/hooks/timeoutable.rb +16 -9
  63. data/lib/devise/hooks/trackable.rb +3 -1
  64. data/lib/devise/mailers/helpers.rb +15 -12
  65. data/lib/devise/mapping.rb +8 -2
  66. data/lib/devise/models.rb +3 -1
  67. data/lib/devise/models/authenticatable.rb +63 -36
  68. data/lib/devise/models/confirmable.rb +121 -41
  69. data/lib/devise/models/database_authenticatable.rb +66 -23
  70. data/lib/devise/models/lockable.rb +30 -17
  71. data/lib/devise/models/omniauthable.rb +3 -1
  72. data/lib/devise/models/recoverable.rb +62 -26
  73. data/lib/devise/models/registerable.rb +2 -0
  74. data/lib/devise/models/rememberable.rb +62 -33
  75. data/lib/devise/models/timeoutable.rb +4 -8
  76. data/lib/devise/models/trackable.rb +12 -3
  77. data/lib/devise/models/validatable.rb +16 -9
  78. data/lib/devise/modules.rb +12 -10
  79. data/lib/devise/omniauth.rb +2 -0
  80. data/lib/devise/omniauth/config.rb +2 -0
  81. data/lib/devise/omniauth/url_helpers.rb +14 -5
  82. data/lib/devise/orm/active_record.rb +5 -1
  83. data/lib/devise/orm/mongoid.rb +6 -2
  84. data/lib/devise/parameter_filter.rb +2 -0
  85. data/lib/devise/parameter_sanitizer.rb +131 -69
  86. data/lib/devise/rails.rb +10 -13
  87. data/lib/devise/rails/routes.rb +147 -116
  88. data/lib/devise/rails/warden_compat.rb +3 -10
  89. data/lib/devise/secret_key_finder.rb +25 -0
  90. data/lib/devise/strategies/authenticatable.rb +20 -9
  91. data/lib/devise/strategies/base.rb +3 -1
  92. data/lib/devise/strategies/database_authenticatable.rb +8 -5
  93. data/lib/devise/strategies/rememberable.rb +15 -3
  94. data/lib/devise/test/controller_helpers.rb +165 -0
  95. data/lib/devise/test/integration_helpers.rb +63 -0
  96. data/lib/devise/test_helpers.rb +7 -124
  97. data/lib/devise/time_inflector.rb +4 -2
  98. data/lib/devise/token_generator.rb +3 -41
  99. data/lib/devise/version.rb +3 -1
  100. data/lib/generators/active_record/devise_generator.rb +47 -10
  101. data/lib/generators/active_record/templates/migration.rb +9 -7
  102. data/lib/generators/active_record/templates/migration_existing.rb +9 -7
  103. data/lib/generators/devise/controllers_generator.rb +46 -0
  104. data/lib/generators/devise/devise_generator.rb +9 -5
  105. data/lib/generators/devise/install_generator.rb +22 -0
  106. data/lib/generators/devise/orm_helpers.rb +8 -19
  107. data/lib/generators/devise/views_generator.rb +51 -28
  108. data/lib/generators/mongoid/devise_generator.rb +22 -19
  109. data/lib/generators/templates/README +5 -12
  110. data/lib/generators/templates/controllers/README +14 -0
  111. data/lib/generators/templates/controllers/confirmations_controller.rb +30 -0
  112. data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +30 -0
  113. data/lib/generators/templates/controllers/passwords_controller.rb +34 -0
  114. data/lib/generators/templates/controllers/registrations_controller.rb +62 -0
  115. data/lib/generators/templates/controllers/sessions_controller.rb +27 -0
  116. data/lib/generators/templates/controllers/unlocks_controller.rb +30 -0
  117. data/lib/generators/templates/devise.rb +64 -35
  118. data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
  119. data/lib/generators/templates/markerb/email_changed.markerb +7 -0
  120. data/lib/generators/templates/markerb/password_change.markerb +3 -0
  121. data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
  122. data/lib/generators/templates/markerb/unlock_instructions.markerb +1 -1
  123. data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +2 -2
  124. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +4 -4
  125. data/lib/generators/templates/simple_form_for/passwords/new.html.erb +2 -2
  126. data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +6 -6
  127. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +4 -4
  128. data/lib/generators/templates/simple_form_for/sessions/new.html.erb +6 -6
  129. data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +2 -2
  130. data/test/controllers/custom_registrations_controller_test.rb +42 -0
  131. data/test/controllers/custom_strategy_test.rb +10 -6
  132. data/test/controllers/helper_methods_test.rb +24 -0
  133. data/test/controllers/helpers_test.rb +88 -40
  134. data/test/controllers/inherited_controller_i18n_messages_test.rb +53 -0
  135. data/test/controllers/internal_helpers_test.rb +31 -22
  136. data/test/controllers/load_hooks_controller_test.rb +21 -0
  137. data/test/controllers/passwords_controller_test.rb +8 -5
  138. data/test/controllers/sessions_controller_test.rb +42 -33
  139. data/test/controllers/url_helpers_test.rb +13 -5
  140. data/test/delegator_test.rb +3 -1
  141. data/test/devise_test.rb +34 -19
  142. data/test/failure_app_test.rb +150 -42
  143. data/test/generators/active_record_generator_test.rb +58 -31
  144. data/test/generators/controllers_generator_test.rb +50 -0
  145. data/test/generators/devise_generator_test.rb +4 -2
  146. data/test/generators/install_generator_test.rb +16 -3
  147. data/test/generators/mongoid_generator_test.rb +5 -3
  148. data/test/generators/views_generator_test.rb +40 -2
  149. data/test/helpers/devise_helper_test.rb +20 -20
  150. data/test/integration/authenticatable_test.rb +134 -141
  151. data/test/integration/confirmable_test.rb +109 -67
  152. data/test/integration/database_authenticatable_test.rb +36 -23
  153. data/test/integration/http_authenticatable_test.rb +29 -20
  154. data/test/integration/lockable_test.rb +52 -49
  155. data/test/integration/mounted_engine_test.rb +38 -0
  156. data/test/integration/omniauthable_test.rb +30 -15
  157. data/test/integration/recoverable_test.rb +76 -61
  158. data/test/integration/registerable_test.rb +107 -91
  159. data/test/integration/rememberable_test.rb +82 -30
  160. data/test/integration/timeoutable_test.rb +48 -40
  161. data/test/integration/trackable_test.rb +15 -8
  162. data/test/mailers/confirmation_instructions_test.rb +16 -14
  163. data/test/mailers/email_changed_test.rb +132 -0
  164. data/test/mailers/mailer_test.rb +20 -0
  165. data/test/mailers/reset_password_instructions_test.rb +13 -11
  166. data/test/mailers/unlock_instructions_test.rb +12 -10
  167. data/test/mapping_test.rb +15 -6
  168. data/test/models/authenticatable_test.rb +15 -3
  169. data/test/models/confirmable_test.rb +190 -95
  170. data/test/models/database_authenticatable_test.rb +75 -41
  171. data/test/models/lockable_test.rb +115 -61
  172. data/test/models/omniauthable_test.rb +3 -1
  173. data/test/models/recoverable_test.rb +116 -37
  174. data/test/models/registerable_test.rb +3 -1
  175. data/test/models/rememberable_test.rb +95 -94
  176. data/test/models/serializable_test.rb +19 -8
  177. data/test/models/timeoutable_test.rb +10 -8
  178. data/test/models/trackable_test.rb +50 -1
  179. data/test/models/validatable_test.rb +24 -30
  180. data/test/models_test.rb +19 -8
  181. data/test/omniauth/config_test.rb +15 -11
  182. data/test/omniauth/url_helpers_test.rb +8 -9
  183. data/test/orm/active_record.rb +16 -2
  184. data/test/orm/mongoid.rb +4 -2
  185. data/test/parameter_sanitizer_test.rb +53 -57
  186. data/test/rails_app/app/active_record/admin.rb +2 -0
  187. data/test/rails_app/app/active_record/shim.rb +3 -1
  188. data/test/rails_app/app/active_record/user.rb +14 -0
  189. data/test/rails_app/app/active_record/user_on_engine.rb +9 -0
  190. data/test/rails_app/app/active_record/user_on_main_app.rb +9 -0
  191. data/test/rails_app/app/active_record/user_with_validations.rb +12 -0
  192. data/test/rails_app/app/active_record/user_without_email.rb +10 -0
  193. data/test/rails_app/app/controllers/admins/sessions_controller.rb +3 -1
  194. data/test/rails_app/app/controllers/admins_controller.rb +3 -6
  195. data/test/rails_app/app/controllers/application_controller.rb +7 -3
  196. data/test/rails_app/app/controllers/application_with_fake_engine.rb +32 -0
  197. data/test/rails_app/app/controllers/custom/registrations_controller.rb +33 -0
  198. data/test/rails_app/app/controllers/home_controller.rb +7 -1
  199. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +3 -1
  200. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +3 -1
  201. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +7 -5
  202. data/test/rails_app/app/controllers/users_controller.rb +8 -6
  203. data/test/rails_app/app/helpers/application_helper.rb +2 -0
  204. data/test/rails_app/app/mailers/users/from_proc_mailer.rb +5 -0
  205. data/test/rails_app/app/mailers/users/mailer.rb +3 -10
  206. data/test/rails_app/app/mailers/users/reply_to_mailer.rb +6 -0
  207. data/test/rails_app/app/mongoid/admin.rb +13 -11
  208. data/test/rails_app/app/mongoid/shim.rb +4 -2
  209. data/test/rails_app/app/mongoid/user.rb +30 -19
  210. data/test/rails_app/app/mongoid/user_on_engine.rb +41 -0
  211. data/test/rails_app/app/mongoid/user_on_main_app.rb +41 -0
  212. data/test/rails_app/app/mongoid/user_with_validations.rb +37 -0
  213. data/test/rails_app/app/mongoid/user_without_email.rb +35 -0
  214. data/test/rails_app/app/views/admins/sessions/new.html.erb +1 -1
  215. data/test/rails_app/app/views/home/admin_dashboard.html.erb +1 -1
  216. data/test/rails_app/app/views/home/index.html.erb +1 -1
  217. data/test/rails_app/app/views/home/join.html.erb +1 -1
  218. data/test/rails_app/app/views/home/user_dashboard.html.erb +1 -1
  219. data/test/rails_app/app/views/layouts/application.html.erb +1 -1
  220. data/test/rails_app/config/application.rb +13 -5
  221. data/test/rails_app/config/boot.rb +17 -4
  222. data/test/rails_app/config/environment.rb +2 -0
  223. data/test/rails_app/config/environments/development.rb +2 -0
  224. data/test/rails_app/config/environments/production.rb +10 -2
  225. data/test/rails_app/config/environments/test.rb +14 -3
  226. data/test/rails_app/config/initializers/backtrace_silencers.rb +2 -0
  227. data/test/rails_app/config/initializers/devise.rb +22 -21
  228. data/test/rails_app/config/initializers/inflections.rb +2 -0
  229. data/test/rails_app/config/initializers/secret_token.rb +3 -6
  230. data/test/rails_app/config/initializers/session_store.rb +2 -0
  231. data/test/rails_app/config/routes.rb +67 -43
  232. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +16 -10
  233. data/test/rails_app/db/schema.rb +2 -0
  234. data/test/rails_app/lib/shared_admin.rb +10 -4
  235. data/test/rails_app/lib/shared_user.rb +4 -1
  236. data/test/rails_app/lib/shared_user_without_email.rb +28 -0
  237. data/test/rails_app/lib/shared_user_without_omniauth.rb +15 -0
  238. data/test/rails_test.rb +11 -0
  239. data/test/routes_test.rb +92 -61
  240. data/test/secret_key_finder_test.rb +97 -0
  241. data/test/support/action_controller/record_identifier.rb +12 -0
  242. data/test/support/assertions.rb +4 -14
  243. data/test/support/helpers.rb +23 -10
  244. data/test/support/http_method_compatibility.rb +53 -0
  245. data/test/support/integration.rb +19 -16
  246. data/test/support/mongoid.yml +6 -0
  247. data/test/support/webrat/integrations/rails.rb +11 -0
  248. data/test/{test_helpers_test.rb → test/controller_helpers_test.rb} +60 -40
  249. data/test/test/integration_helpers_test.rb +34 -0
  250. data/test/test_helper.rb +9 -0
  251. data/test/test_models.rb +8 -6
  252. metadata +123 -53
  253. data/gemfiles/Gemfile.rails-3.2.x +0 -31
  254. data/gemfiles/Gemfile.rails-3.2.x.lock +0 -159
@@ -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.
@@ -17,7 +19,7 @@ module Devise
17
19
  #
18
20
  # * +remember_for+: the time you want the user will be remembered without
19
21
  # asking for credentials. After this time the user will be blocked and
20
- # will have to enter his credentials again. This configuration is also
22
+ # will have to enter their credentials again. This configuration is also
21
23
  # used to calculate the expires time for the cookie created to remember
22
24
  # the user. By default remember_for is 2.weeks.
23
25
  #
@@ -39,46 +41,42 @@ 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
- # unless remember_across_browsers is true and the user already has a valid token.
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)
53
- save(:validate => false) if self.changed?
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
+ save(validate: false) if self.changed?
54
54
  end
55
55
 
56
56
  # If the record is persisted, remove the remember token (but only if
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=)
61
- self.remember_created_at = nil
62
- save(:validate => false)
60
+ self.remember_token = nil if respond_to?(:remember_token)
61
+ self.remember_created_at = nil if self.class.expire_all_remember_me_on_sign_out
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
76
74
  if respond_to?(:remember_token)
77
75
  remember_token
78
- elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt)
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."
@@ -89,40 +87,71 @@ module Devise
89
87
  self.class.rememberable_options
90
88
  end
91
89
 
92
- protected
90
+ # A callback initiated after successfully being remembered. This can be
91
+ # used to insert your own logic that is only run after the user is
92
+ # remembered.
93
+ #
94
+ # Example:
95
+ #
96
+ # def after_remembered
97
+ # self.update_attribute(:invite_code, nil)
98
+ # end
99
+ #
100
+ def after_remembered
101
+ end
102
+
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/plataformatec/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
93
109
 
94
- def generate_remember_token? #:nodoc:
95
- 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)
96
120
  end
97
121
 
98
- # Generate a timestamp if extend_remember_period is true, if no remember_token
99
- # exists, or if an existing remember token has expired.
100
- def generate_remember_timestamp?(extend_period) #:nodoc:
101
- extend_period || remember_created_at.nil? || 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
102
130
  end
103
131
 
104
132
  module ClassMethods
105
133
  # Create the cookie key using the record id and remember_token
106
134
  def serialize_into_cookie(record)
107
- [record.to_key, record.rememberable_value]
135
+ [record.to_key, record.rememberable_value, Time.now.utc.to_f.to_s]
108
136
  end
109
137
 
110
138
  # Recreate the user based on the stored cookie
111
- def serialize_from_cookie(id, remember_token)
139
+ def serialize_from_cookie(*args)
140
+ id, token, generated_at = *args
141
+
112
142
  record = to_adapter.get(id)
113
- record if record && !record.remember_expired? &&
114
- Devise.secure_compare(record.rememberable_value, remember_token)
143
+ record if record && record.remember_me?(token, generated_at)
115
144
  end
116
145
 
117
146
  # Generate a token checking if one does not already exist in the database.
118
147
  def remember_token #:nodoc:
119
148
  loop do
120
149
  token = Devise.friendly_token
121
- break token unless to_adapter.find_first({ :remember_token => token })
150
+ break token unless to_adapter.find_first({ remember_token: token })
122
151
  end
123
152
  end
124
153
 
125
- Devise::Models.config(self, :remember_for, :extend_remember_period, :rememberable_options)
154
+ Devise::Models.config(self, :remember_for, :extend_remember_period, :rememberable_options, :expire_all_remember_me_on_sign_out)
126
155
  end
127
156
  end
128
157
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'devise/hooks/timeoutable'
2
4
 
3
5
  module Devise
4
6
  module Models
5
- # Timeoutable takes care of verifyng whether a user session has already
7
+ # Timeoutable takes care of verifying whether a user session has already
6
8
  # expired or not. When a session expires after the configured time, the user
7
- # will be asked for credentials again, it means, he/she will be redirected
9
+ # will be asked for credentials again, it means, they will be redirected
8
10
  # to the sign in page.
9
11
  #
10
12
  # == Options
@@ -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
@@ -15,7 +17,7 @@ module Devise
15
17
  [:current_sign_in_at, :current_sign_in_ip, :last_sign_in_at, :last_sign_in_ip, :sign_in_count]
16
18
  end
17
19
 
18
- def update_tracked_fields!(request)
20
+ def update_tracked_fields(request)
19
21
  old_current, new_current = self.current_sign_in_at, Time.now.utc
20
22
  self.last_sign_in_at = old_current || new_current
21
23
  self.current_sign_in_at = new_current
@@ -26,9 +28,16 @@ module Devise
26
28
 
27
29
  self.sign_in_count ||= 0
28
30
  self.sign_in_count += 1
31
+ end
32
+
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/plataformatec/devise/issues/4673 for more details.
37
+ return if new_record?
29
38
 
30
- save(:validate => false) or raise "Devise trackable could not save #{inspect}." \
31
- "Please make sure a model using trackable can be saved at sign in."
39
+ update_tracked_fields(request)
40
+ save(validate: false)
32
41
  end
33
42
  end
34
43
  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,12 +12,12 @@ 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..128.
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.
17
- VALIDATIONS = [ :validates_presence_of, :validates_uniqueness_of, :validates_format_of,
18
- :validates_confirmation_of, :validates_length_of ].freeze
19
+ VALIDATIONS = [:validates_presence_of, :validates_uniqueness_of, :validates_format_of,
20
+ :validates_confirmation_of, :validates_length_of].freeze
19
21
 
20
22
  def self.required_fields(klass)
21
23
  []
@@ -26,13 +28,18 @@ module Devise
26
28
  assert_validations_api!(base)
27
29
 
28
30
  base.class_eval do
29
- 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?
31
+ validates_presence_of :email, if: :email_required?
32
+ if Devise.activerecord51?
33
+ validates_uniqueness_of :email, allow_blank: 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
- validates_presence_of :password, :if => :password_required?
34
- validates_confirmation_of :password, :if => :password_required?
35
- validates_length_of :password, :within => password_length, :allow_blank => true
40
+ validates_presence_of :password, if: :password_required?
41
+ validates_confirmation_of :password, if: :password_required?
42
+ validates_length_of :password, within: password_length, allow_blank: true
36
43
  end
37
44
  end
38
45
 
@@ -1,28 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/core_ext/object/with_options'
2
4
 
3
- Devise.with_options :model => true do |d|
5
+ Devise.with_options model: true do |d|
4
6
  # Strategies first
5
- d.with_options :strategy => true do |s|
7
+ d.with_options strategy: true do |s|
6
8
  routes = [nil, :new, :destroy]
7
- s.add_module :database_authenticatable, :controller => :sessions, :route => { :session => routes }
8
- s.add_module :rememberable, :no_input => true
9
+ s.add_module :database_authenticatable, controller: :sessions, route: { session: routes }
10
+ s.add_module :rememberable, no_input: true
9
11
  end
10
12
 
11
13
  # Other authentications
12
- d.add_module :omniauthable, :controller => :omniauth_callbacks, :route => :omniauth_callback
14
+ d.add_module :omniauthable, controller: :omniauth_callbacks, route: :omniauth_callback
13
15
 
14
16
  # Misc after
15
17
  routes = [nil, :new, :edit]
16
- d.add_module :recoverable, :controller => :passwords, :route => { :password => routes }
17
- d.add_module :registerable, :controller => :registrations, :route => { :registration => (routes << :cancel) }
18
+ d.add_module :recoverable, controller: :passwords, route: { password: routes }
19
+ d.add_module :registerable, controller: :registrations, route: { registration: (routes << :cancel) }
18
20
  d.add_module :validatable
19
21
 
20
22
  # The ones which can sign out after
21
23
  routes = [nil, :new]
22
- d.add_module :confirmable, :controller => :confirmations, :route => { :confirmation => routes }
23
- d.add_module :lockable, :controller => :unlocks, :route => { :unlock => routes }
24
+ d.add_module :confirmable, controller: :confirmations, route: { confirmation: routes }
25
+ d.add_module :lockable, controller: :unlocks, route: { unlock: routes }
24
26
  d.add_module :timeoutable
25
27
 
26
28
  # Stats for last, so we make sure the user is really signed in
27
29
  d.add_module :trackable
28
- end
30
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require "omniauth"
3
5
  require "omniauth/version"
@@ -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
- 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
- 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,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)
@@ -1,99 +1,161 @@
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
29
-
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
36
- end
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
37
114
 
38
- def default_params
39
- params.fetch(resource_name, {})
40
- end
41
- end
115
+ if keys.present?
116
+ @permitted[action] ||= @auth_keys.dup
117
+ @permitted[action].concat(keys)
118
+ end
42
119
 
43
- class ParameterSanitizer < BaseSanitizer
44
- def initialize(*)
45
- super
46
- @permitted = Hash.new { |h,k| h[k] = attributes_for(k) }
120
+ if except.present?
121
+ @permitted[action] ||= @auth_keys.dup
122
+ @permitted[action] = @permitted[action] - except
123
+ end
47
124
  end
48
125
 
49
- def sign_in
50
- permit self.for(:sign_in)
51
- end
126
+ private
52
127
 
53
- def sign_up
54
- permit self.for(:sign_up)
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
55
135
  end
56
136
 
57
- def account_update
58
- permit self.for(:account_update)
137
+ def default_params
138
+ @params.fetch(@resource_name, {})
59
139
  end
60
140
 
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))
141
+ def permit_keys(parameters, keys)
142
+ parameters.permit(*keys)
67
143
  end
68
144
 
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
145
+ def extract_auth_keys(klass)
146
+ auth_keys = klass.authentication_keys
74
147
 
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
148
+ auth_keys.respond_to?(:keys) ? auth_keys.keys : auth_keys
81
149
  end
82
150
 
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
151
+ def unknown_action!(action)
152
+ raise NotImplementedError, <<-MESSAGE.strip_heredoc
153
+ "Devise doesn't know how to sanitize parameters for '#{action}'".
154
+ If you want to define a new set of parameters to be sanitized use the
155
+ `permit` method first:
93
156
 
94
- def auth_keys
95
- @auth_keys ||= @resource_class.authentication_keys.respond_to?(:keys) ?
96
- @resource_class.authentication_keys.keys : @resource_class.authentication_keys
157
+ devise_parameter_sanitizer.permit(:#{action}, keys: [:param1, :param2, :param3])
158
+ MESSAGE
97
159
  end
98
160
  end
99
161
  end