devise 3.2.0 → 3.3.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.

Potentially problematic release.


This version of devise might be problematic. Click here for more details.

Files changed (187) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +18 -3
  3. data/CHANGELOG.md +82 -9
  4. data/CONTRIBUTING.md +2 -2
  5. data/Gemfile +7 -9
  6. data/Gemfile.lock +102 -96
  7. data/MIT-LICENSE +1 -1
  8. data/README.md +94 -42
  9. data/Rakefile +1 -1
  10. data/app/controllers/devise/confirmations_controller.rb +5 -3
  11. data/app/controllers/devise/omniauth_callbacks_controller.rb +2 -2
  12. data/app/controllers/devise/passwords_controller.rb +5 -3
  13. data/app/controllers/devise/registrations_controller.rb +26 -10
  14. data/app/controllers/devise/sessions_controller.rb +39 -14
  15. data/app/controllers/devise/unlocks_controller.rb +4 -2
  16. data/app/controllers/devise_controller.rb +6 -6
  17. data/app/helpers/devise_helper.rb +2 -2
  18. data/app/views/devise/confirmations/new.html.erb +2 -2
  19. data/app/views/devise/mailer/confirmation_instructions.html.erb +1 -1
  20. data/app/views/devise/mailer/reset_password_instructions.html.erb +1 -1
  21. data/app/views/devise/mailer/unlock_instructions.html.erb +1 -1
  22. data/app/views/devise/passwords/edit.html.erb +3 -3
  23. data/app/views/devise/passwords/new.html.erb +2 -2
  24. data/app/views/devise/registrations/edit.html.erb +6 -6
  25. data/app/views/devise/registrations/new.html.erb +5 -5
  26. data/app/views/devise/sessions/new.html.erb +5 -5
  27. data/app/views/devise/shared/_links.erb +1 -1
  28. data/app/views/devise/unlocks/new.html.erb +2 -2
  29. data/config/locales/en.yml +17 -16
  30. data/devise.gemspec +2 -2
  31. data/gemfiles/{Gemfile.rails-3.2.x → Gemfile.rails-3.2-stable} +8 -10
  32. data/gemfiles/Gemfile.rails-3.2-stable.lock +166 -0
  33. data/gemfiles/Gemfile.rails-4.0-stable +29 -0
  34. data/gemfiles/Gemfile.rails-4.0-stable.lock +162 -0
  35. data/gemfiles/Gemfile.rails-head +32 -0
  36. data/gemfiles/Gemfile.rails-head.lock +190 -0
  37. data/lib/devise/controllers/helpers.rb +84 -29
  38. data/lib/devise/controllers/rememberable.rb +3 -3
  39. data/lib/devise/controllers/scoped_views.rb +1 -1
  40. data/lib/devise/controllers/sign_in_out.rb +9 -10
  41. data/lib/devise/controllers/store_location.rb +56 -0
  42. data/lib/devise/controllers/url_helpers.rb +3 -1
  43. data/lib/devise/failure_app.rb +12 -10
  44. data/lib/devise/hooks/activatable.rb +5 -6
  45. data/lib/devise/hooks/csrf_cleaner.rb +3 -1
  46. data/lib/devise/hooks/lockable.rb +1 -1
  47. data/lib/devise/hooks/proxy.rb +2 -2
  48. data/lib/devise/hooks/rememberable.rb +2 -2
  49. data/lib/devise/hooks/timeoutable.rb +10 -3
  50. data/lib/devise/hooks/trackable.rb +1 -1
  51. data/lib/devise/mailers/helpers.rb +8 -8
  52. data/lib/devise/mapping.rb +4 -1
  53. data/lib/devise/models/authenticatable.rb +5 -5
  54. data/lib/devise/models/confirmable.rb +14 -14
  55. data/lib/devise/models/database_authenticatable.rb +18 -5
  56. data/lib/devise/models/lockable.rb +14 -11
  57. data/lib/devise/models/omniauthable.rb +1 -1
  58. data/lib/devise/models/recoverable.rb +23 -7
  59. data/lib/devise/models/rememberable.rb +6 -6
  60. data/lib/devise/models/timeoutable.rb +2 -2
  61. data/lib/devise/models/trackable.rb +5 -2
  62. data/lib/devise/models/validatable.rb +6 -6
  63. data/lib/devise/modules.rb +10 -10
  64. data/lib/devise/omniauth/url_helpers.rb +2 -2
  65. data/lib/devise/orm/active_record.rb +1 -1
  66. data/lib/devise/orm/mongoid.rb +1 -1
  67. data/lib/devise/rails/routes.rb +107 -78
  68. data/lib/devise/rails.rb +7 -1
  69. data/lib/devise/strategies/authenticatable.rb +11 -4
  70. data/lib/devise/strategies/base.rb +1 -1
  71. data/lib/devise/strategies/database_authenticatable.rb +7 -4
  72. data/lib/devise/test_helpers.rb +2 -2
  73. data/lib/devise/time_inflector.rb +2 -2
  74. data/lib/devise/version.rb +1 -1
  75. data/lib/devise.rb +15 -10
  76. data/lib/generators/active_record/devise_generator.rb +27 -10
  77. data/lib/generators/active_record/templates/migration.rb +4 -4
  78. data/lib/generators/active_record/templates/migration_existing.rb +4 -4
  79. data/lib/generators/devise/devise_generator.rb +5 -3
  80. data/lib/generators/devise/install_generator.rb +5 -0
  81. data/lib/generators/devise/views_generator.rb +31 -18
  82. data/lib/generators/mongoid/devise_generator.rb +20 -19
  83. data/lib/generators/templates/README +4 -4
  84. data/lib/generators/templates/devise.rb +20 -11
  85. data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
  86. data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
  87. data/lib/generators/templates/markerb/unlock_instructions.markerb +1 -1
  88. data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +2 -2
  89. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +4 -4
  90. data/lib/generators/templates/simple_form_for/passwords/new.html.erb +2 -2
  91. data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +6 -6
  92. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +4 -4
  93. data/lib/generators/templates/simple_form_for/sessions/new.html.erb +4 -4
  94. data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +2 -2
  95. data/script/cached-bundle +49 -0
  96. data/script/s3-put +71 -0
  97. data/test/controllers/custom_registrations_controller_test.rb +35 -0
  98. data/test/controllers/helpers_test.rb +89 -31
  99. data/test/controllers/internal_helpers_test.rb +11 -8
  100. data/test/controllers/passwords_controller_test.rb +1 -1
  101. data/test/controllers/sessions_controller_test.rb +29 -25
  102. data/test/controllers/url_helpers_test.rb +4 -4
  103. data/test/delegator_test.rb +1 -1
  104. data/test/devise_test.rb +21 -8
  105. data/test/failure_app_test.rb +61 -25
  106. data/test/generators/active_record_generator_test.rb +10 -4
  107. data/test/generators/devise_generator_test.rb +2 -2
  108. data/test/generators/mongoid_generator_test.rb +3 -3
  109. data/test/generators/views_generator_test.rb +30 -1
  110. data/test/helpers/devise_helper_test.rb +14 -13
  111. data/test/integration/authenticatable_test.rb +61 -45
  112. data/test/integration/confirmable_test.rb +95 -55
  113. data/test/integration/database_authenticatable_test.rb +16 -16
  114. data/test/integration/http_authenticatable_test.rb +12 -12
  115. data/test/integration/lockable_test.rb +43 -43
  116. data/test/integration/omniauthable_test.rb +3 -3
  117. data/test/integration/recoverable_test.rb +53 -53
  118. data/test/integration/registerable_test.rb +90 -80
  119. data/test/integration/rememberable_test.rb +15 -15
  120. data/test/integration/timeoutable_test.rb +27 -16
  121. data/test/integration/trackable_test.rb +4 -4
  122. data/test/mailers/confirmation_instructions_test.rb +7 -7
  123. data/test/mailers/reset_password_instructions_test.rb +6 -6
  124. data/test/mailers/unlock_instructions_test.rb +5 -5
  125. data/test/mapping_test.rb +3 -3
  126. data/test/models/authenticatable_test.rb +3 -3
  127. data/test/models/confirmable_test.rb +32 -32
  128. data/test/models/database_authenticatable_test.rb +32 -27
  129. data/test/models/lockable_test.rb +57 -33
  130. data/test/models/recoverable_test.rb +34 -22
  131. data/test/models/rememberable_test.rb +29 -14
  132. data/test/models/serializable_test.rb +8 -8
  133. data/test/models/timeoutable_test.rb +1 -1
  134. data/test/models/trackable_test.rb +28 -0
  135. data/test/models/validatable_test.rb +13 -13
  136. data/test/omniauth/config_test.rb +4 -4
  137. data/test/omniauth/url_helpers_test.rb +3 -3
  138. data/test/orm/mongoid.rb +1 -1
  139. data/test/parameter_sanitizer_test.rb +1 -1
  140. data/test/rails_app/app/active_record/shim.rb +1 -1
  141. data/test/rails_app/app/active_record/user_on_engine.rb +7 -0
  142. data/test/rails_app/app/active_record/user_on_main_app.rb +7 -0
  143. data/test/rails_app/app/controllers/admins/sessions_controller.rb +1 -1
  144. data/test/rails_app/app/controllers/admins_controller.rb +1 -1
  145. data/test/rails_app/app/controllers/application_controller.rb +5 -2
  146. data/test/rails_app/app/controllers/application_with_fake_engine.rb +30 -0
  147. data/test/rails_app/app/controllers/custom/registrations_controller.rb +21 -0
  148. data/test/rails_app/app/controllers/home_controller.rb +1 -1
  149. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +1 -1
  150. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +1 -1
  151. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +4 -4
  152. data/test/rails_app/app/controllers/users_controller.rb +6 -6
  153. data/test/rails_app/app/mailers/users/mailer.rb +4 -4
  154. data/test/rails_app/app/mongoid/admin.rb +11 -11
  155. data/test/rails_app/app/mongoid/shim.rb +2 -2
  156. data/test/rails_app/app/mongoid/user.rb +19 -19
  157. data/test/rails_app/app/mongoid/user_on_engine.rb +39 -0
  158. data/test/rails_app/app/mongoid/user_on_main_app.rb +39 -0
  159. data/test/rails_app/app/views/admins/sessions/new.html.erb +1 -1
  160. data/test/rails_app/app/views/home/admin_dashboard.html.erb +1 -1
  161. data/test/rails_app/app/views/home/index.html.erb +1 -1
  162. data/test/rails_app/app/views/home/join.html.erb +1 -1
  163. data/test/rails_app/app/views/home/user_dashboard.html.erb +1 -1
  164. data/test/rails_app/app/views/layouts/application.html.erb +1 -1
  165. data/test/rails_app/config/application.rb +2 -2
  166. data/test/rails_app/config/boot.rb +7 -1
  167. data/test/rails_app/config/environments/development.rb +0 -4
  168. data/test/rails_app/config/environments/production.rb +0 -4
  169. data/test/rails_app/config/initializers/devise.rb +7 -5
  170. data/test/rails_app/config/initializers/secret_token.rb +1 -1
  171. data/test/rails_app/config/routes.rb +60 -42
  172. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +7 -7
  173. data/test/rails_app/db/schema.rb +21 -17
  174. data/test/rails_app/lib/shared_admin.rb +4 -4
  175. data/test/rails_app/lib/shared_user.rb +1 -1
  176. data/test/rails_app/lib/shared_user_without_omniauth.rb +13 -0
  177. data/test/routes_test.rb +72 -58
  178. data/test/support/action_controller/record_identifier.rb +10 -0
  179. data/test/support/assertions.rb +2 -3
  180. data/test/support/helpers.rb +4 -4
  181. data/test/support/integration.rb +14 -14
  182. data/test/support/mongoid.yml +6 -0
  183. data/test/test_helper.rb +2 -7
  184. data/test/test_helpers_test.rb +25 -35
  185. data/test/test_models.rb +12 -5
  186. metadata +53 -38
  187. data/gemfiles/Gemfile.rails-3.2.x.lock +0 -159
@@ -47,7 +47,9 @@ module Devise
47
47
  class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1
48
48
  def #{method}(resource_or_scope, *args)
49
49
  scope = Devise::Mapping.find_scope!(resource_or_scope)
50
- _devise_route_context.send("#{action}\#{scope}_#{module_name}_#{path_or_url}", *args)
50
+ router_name = Devise.mappings[scope].router_name
51
+ context = router_name ? send(router_name) : _devise_route_context
52
+ context.send("#{action}\#{scope}_#{module_name}_#{path_or_url}", *args)
51
53
  end
52
54
  URL_HELPERS
53
55
  end
@@ -13,7 +13,9 @@ module Devise
13
13
  include Rails.application.routes.url_helpers
14
14
  include Rails.application.routes.mounted_helpers
15
15
 
16
- delegate :flash, :to => :request
16
+ include Devise::Controllers::StoreLocation
17
+
18
+ delegate :flash, to: :request
17
19
 
18
20
  def self.call(env)
19
21
  @respond ||= action(:respond)
@@ -94,15 +96,15 @@ module Devise
94
96
  request.referrer
95
97
  end
96
98
 
97
- path || scope_path
99
+ path || scope_url
98
100
  else
99
- scope_path
101
+ scope_url
100
102
  end
101
103
  end
102
104
 
103
- def scope_path
105
+ def scope_url
104
106
  opts = {}
105
- route = :"new_#{scope}_session_path"
107
+ route = :"new_#{scope}_session_url"
106
108
  opts[:format] = request_format unless skip_format?
107
109
 
108
110
  config = Rails.application.config
@@ -112,8 +114,8 @@ module Devise
112
114
 
113
115
  if context.respond_to?(route)
114
116
  context.send(route, opts)
115
- elsif respond_to?(:root_path)
116
- root_path(opts)
117
+ elsif respond_to?(:root_url)
118
+ root_url(opts)
117
119
  else
118
120
  "/"
119
121
  end
@@ -149,9 +151,9 @@ module Devise
149
151
  return i18n_message unless request_format
150
152
  method = "to_#{request_format}"
151
153
  if method == "to_xml"
152
- { :error => i18n_message }.to_xml(:root => "errors")
154
+ { error: i18n_message }.to_xml(root: "errors")
153
155
  elsif {}.respond_to?(method)
154
- { :error => i18n_message }.send(method)
156
+ { error: i18n_message }.send(method)
155
157
  else
156
158
  i18n_message
157
159
  end
@@ -189,7 +191,7 @@ module Devise
189
191
  # yet, but we still need to store the uri based on scope, so different scopes
190
192
  # would never use the same uri to redirect.
191
193
  def store_location!
192
- session["#{scope}_return_to"] = attempted_path if request.get? && !http_auth?
194
+ store_location_for(scope, attempted_path) if request.get? && !http_auth?
193
195
  end
194
196
 
195
197
  def is_navigational_format?
@@ -1,11 +1,10 @@
1
- # Deny user access whenever his account is not active yet. All strategies that inherits from
2
- # Devise::Strategies::Authenticatable and uses the validate already check if the user is active_for_authentication?
3
- # before actively signing him in. However, we need this as hook to validate the user activity
4
- # in each request and in case the user is using other strategies beside Devise ones.
1
+ # Deny user access whenever their account is not active yet.
2
+ # We need this as hook to validate the user activity on each request
3
+ # and in case the user is using other strategies beside Devise ones.
5
4
  Warden::Manager.after_set_user do |record, warden, options|
6
5
  if record && record.respond_to?(:active_for_authentication?) && !record.active_for_authentication?
7
6
  scope = options[:scope]
8
7
  warden.logout(scope)
9
- throw :warden, :scope => scope, :message => record.inactive_message
8
+ throw :warden, scope: scope, message: record.inactive_message
10
9
  end
11
- end
10
+ end
@@ -1,5 +1,7 @@
1
1
  Warden::Manager.after_authentication do |record, warden, options|
2
- if Devise.clean_up_csrf_token_on_authentication
2
+ clean_up_for_winning_strategy = !warden.winning_strategy.respond_to?(:clean_up_csrf?) ||
3
+ warden.winning_strategy.clean_up_csrf?
4
+ if Devise.clean_up_csrf_token_on_authentication && clean_up_for_winning_strategy
3
5
  warden.request.session.try(:delete, :_csrf_token)
4
6
  end
5
7
  end
@@ -1,6 +1,6 @@
1
1
  # After each sign in, if resource responds to failed_attempts, sets it to 0
2
2
  # This is only triggered when the user is explicitly set (with set_user)
3
- Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
3
+ Warden::Manager.after_set_user except: :fetch do |record, warden, options|
4
4
  if record.respond_to?(:failed_attempts) && warden.authenticated?(options[:scope])
5
5
  record.update_attribute(:failed_attempts, 0) unless record.failed_attempts.to_i.zero?
6
6
  end
@@ -7,7 +7,7 @@ module Devise
7
7
  include Devise::Controllers::SignInOut
8
8
 
9
9
  attr_reader :warden
10
- delegate :cookies, :env, :to => :warden
10
+ delegate :cookies, :env, to: :warden
11
11
 
12
12
  def initialize(warden)
13
13
  @warden = warden
@@ -18,4 +18,4 @@ module Devise
18
18
  end
19
19
  end
20
20
  end
21
- end
21
+ end
@@ -1,7 +1,7 @@
1
- Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
1
+ Warden::Manager.after_set_user except: :fetch do |record, warden, options|
2
2
  scope = options[:scope]
3
3
  if record.respond_to?(:remember_me) && options[:store] != false &&
4
4
  record.remember_me && warden.authenticated?(scope)
5
5
  Devise::Hooks::Proxy.new(warden).remember_me(record)
6
6
  end
7
- end
7
+ end
@@ -9,20 +9,27 @@ Warden::Manager.after_set_user do |record, warden, options|
9
9
 
10
10
  if record && record.respond_to?(:timedout?) && warden.authenticated?(scope) && options[:store] != false
11
11
  last_request_at = warden.session(scope)['last_request_at']
12
+
13
+ if last_request_at.is_a? Integer
14
+ last_request_at = Time.at(last_request_at).utc
15
+ elsif last_request_at.is_a? String
16
+ last_request_at = Time.parse(last_request_at)
17
+ end
18
+
12
19
  proxy = Devise::Hooks::Proxy.new(warden)
13
20
 
14
21
  if record.timedout?(last_request_at) && !env['devise.skip_timeout']
15
- Devise.sign_out_all_scopes ? proxy.sign_out : sign_out(scope)
22
+ Devise.sign_out_all_scopes ? proxy.sign_out : proxy.sign_out(scope)
16
23
 
17
24
  if record.respond_to?(:expire_auth_token_on_timeout) && record.expire_auth_token_on_timeout
18
25
  record.reset_authentication_token!
19
26
  end
20
27
 
21
- throw :warden, :scope => scope, :message => :timeout
28
+ throw :warden, scope: scope, message: :timeout
22
29
  end
23
30
 
24
31
  unless env['devise.skip_trackable']
25
- warden.session(scope)['last_request_at'] = Time.now.utc
32
+ warden.session(scope)['last_request_at'] = Time.now.utc.to_i
26
33
  end
27
34
  end
28
35
  end
@@ -2,7 +2,7 @@
2
2
  # This is only triggered when the user is explicitly set (with set_user)
3
3
  # and on authentication. Retrieving the user from session (:fetch) does
4
4
  # not trigger it.
5
- Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
5
+ Warden::Manager.after_set_user except: :fetch do |record, warden, options|
6
6
  if record.respond_to?(:update_tracked_fields!) && warden.authenticated?(options[:scope]) && !warden.request.env['devise.skip_trackable']
7
7
  record.update_tracked_fields!(warden.request)
8
8
  end
@@ -27,12 +27,12 @@ module Devise
27
27
 
28
28
  def headers_for(action, opts)
29
29
  headers = {
30
- :subject => subject_for(action),
31
- :to => resource.email,
32
- :from => mailer_sender(devise_mapping),
33
- :reply_to => mailer_reply_to(devise_mapping),
34
- :template_path => template_paths,
35
- :template_name => action
30
+ subject: subject_for(action),
31
+ to: resource.email,
32
+ from: mailer_sender(devise_mapping),
33
+ reply_to: mailer_reply_to(devise_mapping),
34
+ template_path: template_paths,
35
+ template_name: action
36
36
  }.merge(opts)
37
37
 
38
38
  @email = headers[:to]
@@ -82,8 +82,8 @@ module Devise
82
82
  # subject: '...'
83
83
  #
84
84
  def subject_for(key)
85
- I18n.t(:"#{devise_mapping.name}_subject", :scope => [:devise, :mailer, key],
86
- :default => [:subject, key.to_s.humanize])
85
+ I18n.t(:"#{devise_mapping.name}_subject", scope: [:devise, :mailer, key],
86
+ default: [:subject, key.to_s.humanize])
87
87
  end
88
88
  end
89
89
  end
@@ -23,7 +23,8 @@ module Devise
23
23
  #
24
24
  class Mapping #:nodoc:
25
25
  attr_reader :singular, :scoped_path, :path, :controllers, :path_names,
26
- :class_name, :sign_out_via, :format, :used_routes, :used_helpers, :failure_app
26
+ :class_name, :sign_out_via, :format, :used_routes, :used_helpers,
27
+ :failure_app, :router_name
27
28
 
28
29
  alias :name :singular
29
30
 
@@ -60,6 +61,8 @@ module Devise
60
61
  @sign_out_via = options[:sign_out_via] || Devise.sign_out_via
61
62
  @format = options[:format]
62
63
 
64
+ @router_name = options[:router_name]
65
+
63
66
  default_failure_app(options)
64
67
  default_controllers(options)
65
68
  default_path_names(options)
@@ -29,7 +29,7 @@ module Devise
29
29
  # It also accepts an array specifying the strategies that should allow params authentication.
30
30
  #
31
31
  # * +skip_session_storage+: By default Devise will store the user in session.
32
- # By default is set to :skip_session_storage => [:http_auth].
32
+ # By default is set to skip_session_storage: [:http_auth].
33
33
  #
34
34
  # == active_for_authentication?
35
35
  #
@@ -56,10 +56,10 @@ module Devise
56
56
  BLACKLIST_FOR_SERIALIZATION = [:encrypted_password, :reset_password_token, :reset_password_sent_at,
57
57
  :remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip,
58
58
  :last_sign_in_ip, :password_salt, :confirmation_token, :confirmed_at, :confirmation_sent_at,
59
- :remember_token, :unconfirmed_email, :failed_attempts, :unlock_token, :locked_at, :authentication_token]
59
+ :remember_token, :unconfirmed_email, :failed_attempts, :unlock_token, :locked_at]
60
60
 
61
61
  included do
62
- class_attribute :devise_modules, :instance_writer => false
62
+ class_attribute :devise_modules, instance_writer: false
63
63
  self.devise_modules ||= []
64
64
 
65
65
  before_validation :downcase_keys
@@ -127,7 +127,7 @@ module Devise
127
127
  end
128
128
 
129
129
  # This is an internal method called every time Devise needs
130
- # to send a notification/mail. This can be overriden if you
130
+ # to send a notification/mail. This can be overridden if you
131
131
  # need to customize the e-mail delivery logic. For instance,
132
132
  # if you are using a queue to deliver e-mails (delayed job,
133
133
  # sidekiq, resque, etc), you must add the delivery to the queue
@@ -231,7 +231,7 @@ module Devise
231
231
  # Example:
232
232
  #
233
233
  # def self.find_for_authentication(tainted_conditions)
234
- # find_first_by_auth_conditions(tainted_conditions, :active => true)
234
+ # find_first_by_auth_conditions(tainted_conditions, active: true)
235
235
  # end
236
236
  #
237
237
  # Finally, notice that Devise also queries for users in other scenarios
@@ -9,7 +9,7 @@ module Devise
9
9
  #
10
10
  # Confirmable adds the following options to +devise+:
11
11
  #
12
- # * +allow_unconfirmed_access_for+: the time you want to allow the user to access his account
12
+ # * +allow_unconfirmed_access_for+: the time you want to allow the user to access their account
13
13
  # before confirming it. After this period, the user access is denied. You can
14
14
  # use this to let your user access some features of your application without
15
15
  # confirming the account, but blocking it after a certain period (ie 7 days).
@@ -33,10 +33,10 @@ module Devise
33
33
  include ActionView::Helpers::DateHelper
34
34
 
35
35
  included do
36
- before_create :generate_confirmation_token, :if => :confirmation_required?
37
- after_create :send_on_create_confirmation_instructions, :if => :send_confirmation_notification?
38
- before_update :postpone_email_change_until_confirmation_and_regenerate_confirmation_token, :if => :postpone_email_change?
39
- after_update :send_reconfirmation_instructions, :if => :reconfirmation_required?
36
+ before_create :generate_confirmation_token, if: :confirmation_required?
37
+ after_create :send_on_create_confirmation_instructions, if: :send_confirmation_notification?
38
+ before_update :postpone_email_change_until_confirmation_and_regenerate_confirmation_token, if: :postpone_email_change?
39
+ after_update :send_reconfirmation_instructions, if: :reconfirmation_required?
40
40
  end
41
41
 
42
42
  def initialize(*args, &block)
@@ -60,7 +60,7 @@ module Devise
60
60
  pending_any_confirmation do
61
61
  if confirmation_period_expired?
62
62
  self.errors.add(:email, :confirmation_period_expired,
63
- :period => Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago))
63
+ period: Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago))
64
64
  return false
65
65
  end
66
66
 
@@ -73,9 +73,9 @@ module Devise
73
73
  self.unconfirmed_email = nil
74
74
 
75
75
  # We need to validate in such cases to enforce e-mail uniqueness
76
- save(:validate => true)
76
+ save(validate: true)
77
77
  else
78
- save(:validate => false)
78
+ save(validate: false)
79
79
  end
80
80
 
81
81
  after_confirmation if saved
@@ -98,7 +98,7 @@ module Devise
98
98
  generate_confirmation_token!
99
99
  end
100
100
 
101
- opts = pending_reconfirmation? ? { :to => unconfirmed_email } : { }
101
+ opts = pending_reconfirmation? ? { to: unconfirmed_email } : { }
102
102
  send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
103
103
  end
104
104
 
@@ -152,7 +152,7 @@ module Devise
152
152
  protected
153
153
 
154
154
  # A callback method used to deliver confirmation
155
- # instructions on creation. This can be overriden
155
+ # instructions on creation. This can be overridden
156
156
  # in models to map to a nice sign up e-mail.
157
157
  def send_on_create_confirmation_instructions
158
158
  send_confirmation_instructions
@@ -225,7 +225,7 @@ module Devise
225
225
  end
226
226
 
227
227
  def generate_confirmation_token!
228
- generate_confirmation_token && save(:validate => false)
228
+ generate_confirmation_token && save(validate: false)
229
229
  end
230
230
 
231
231
  def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
@@ -236,17 +236,17 @@ module Devise
236
236
  end
237
237
 
238
238
  def postpone_email_change?
239
- postpone = self.class.reconfirmable && email_changed? && !@bypass_confirmation_postpone && !self.email.blank?
239
+ postpone = self.class.reconfirmable && email_changed? && !@bypass_confirmation_postpone && self.email.present?
240
240
  @bypass_confirmation_postpone = false
241
241
  postpone
242
242
  end
243
243
 
244
244
  def reconfirmation_required?
245
- self.class.reconfirmable && @reconfirmation_required && !self.email.blank?
245
+ self.class.reconfirmable && @reconfirmation_required && self.email.present?
246
246
  end
247
247
 
248
248
  def send_confirmation_notification?
249
- confirmation_required? && !@skip_confirmation_notification && !self.email.blank?
249
+ confirmation_required? && !@skip_confirmation_notification && self.email.present?
250
250
  end
251
251
 
252
252
  def after_confirmation
@@ -4,7 +4,7 @@ require 'bcrypt'
4
4
  module Devise
5
5
  # Digests the password using bcrypt.
6
6
  def self.bcrypt(klass, password)
7
- ::BCrypt::Password.create("#{password}#{klass.pepper}", :cost => klass.stretches).to_s
7
+ ::BCrypt::Password.create("#{password}#{klass.pepper}", cost: klass.stretches).to_s
8
8
  end
9
9
 
10
10
  module Models
@@ -39,7 +39,7 @@ module Devise
39
39
  # Generates password encryption based on the given value.
40
40
  def password=(new_password)
41
41
  @password = new_password
42
- self.encrypted_password = Devise.bcrypt(self.class, @password) if @password.present?
42
+ self.encrypted_password = password_digest(@password) if @password.present?
43
43
  end
44
44
 
45
45
  # Verifies whether an password (ie from sign in) is the user password.
@@ -55,9 +55,13 @@ module Devise
55
55
  self.password = self.password_confirmation = nil
56
56
  end
57
57
 
58
- # Update record attributes when :current_password matches, otherwise returns
59
- # error on :current_password. It also automatically rejects :password and
60
- # :password_confirmation if they are blank.
58
+ # Update record attributes when :current_password matches, otherwise
59
+ # returns error on :current_password.
60
+ #
61
+ # This method also rejects the password field if it is blank (allowing
62
+ # users to change relevant information like the e-mail without changing
63
+ # their password). In case the password field is rejected, the confirmation
64
+ # is also rejected as long as it is also blank.
61
65
  def update_with_password(params, *options)
62
66
  current_password = params.delete(:current_password)
63
67
 
@@ -135,6 +139,15 @@ module Devise
135
139
 
136
140
  protected
137
141
 
142
+ # Digests the password using bcrypt. Custom encryption should override
143
+ # this method to apply their own algorithm.
144
+ #
145
+ # See https://github.com/plataformatec/devise-encryptable for examples
146
+ # of other encryption engines.
147
+ def password_digest(password)
148
+ Devise.bcrypt(self.class, password)
149
+ end
150
+
138
151
  module ClassMethods
139
152
  Devise::Models.config(self, :pepper, :stretches)
140
153
 
@@ -22,7 +22,7 @@ module Devise
22
22
  module Lockable
23
23
  extend ActiveSupport::Concern
24
24
 
25
- delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, :to => "self.class"
25
+ delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, to: "self.class"
26
26
 
27
27
  def self.required_fields(klass)
28
28
  attributes = []
@@ -34,13 +34,16 @@ module Devise
34
34
  end
35
35
 
36
36
  # Lock a user setting its locked_at to actual time.
37
- def lock_access!
37
+ # * +opts+: Hash options if you don't want to send email
38
+ # when you lock access, you could pass the next hash
39
+ # `{ send_instructions: false } as option`.
40
+ def lock_access!(opts = { })
38
41
  self.locked_at = Time.now.utc
39
42
 
40
- if unlock_strategy_enabled?(:email)
43
+ if unlock_strategy_enabled?(:email) && opts.fetch(:send_instructions, true)
41
44
  send_unlock_instructions
42
45
  else
43
- save(:validate => false)
46
+ save(validate: false)
44
47
  end
45
48
  end
46
49
 
@@ -49,7 +52,7 @@ module Devise
49
52
  self.locked_at = nil
50
53
  self.failed_attempts = 0 if respond_to?(:failed_attempts=)
51
54
  self.unlock_token = nil if respond_to?(:unlock_token=)
52
- save(:validate => false)
55
+ save(validate: false)
53
56
  end
54
57
 
55
58
  # Verifies whether a user is locked or not.
@@ -61,7 +64,7 @@ module Devise
61
64
  def send_unlock_instructions
62
65
  raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
63
66
  self.unlock_token = enc
64
- self.save(:validate => false)
67
+ self.save(validate: false)
65
68
  send_devise_notification(:unlock_instructions, raw, {})
66
69
  raw
67
70
  end
@@ -101,7 +104,7 @@ module Devise
101
104
  if attempts_exceeded?
102
105
  lock_access! unless access_locked?
103
106
  else
104
- save(:validate => false)
107
+ save(validate: false)
105
108
  end
106
109
  false
107
110
  end
@@ -112,10 +115,10 @@ module Devise
112
115
  # leaks the existence of an account.
113
116
  if Devise.paranoid
114
117
  super
118
+ elsif access_locked? || (lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?)
119
+ :locked
115
120
  elsif lock_strategy_enabled?(:failed_attempts) && last_attempt?
116
121
  :last_attempt
117
- elsif lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?
118
- :locked
119
122
  else
120
123
  super
121
124
  end
@@ -124,11 +127,11 @@ module Devise
124
127
  protected
125
128
 
126
129
  def attempts_exceeded?
127
- self.failed_attempts > self.class.maximum_attempts
130
+ self.failed_attempts >= self.class.maximum_attempts
128
131
  end
129
132
 
130
133
  def last_attempt?
131
- self.failed_attempts == self.class.maximum_attempts
134
+ self.failed_attempts == self.class.maximum_attempts - 1
132
135
  end
133
136
 
134
137
  # Tells if the lock is expired if :time unlock strategy is active
@@ -10,7 +10,7 @@ module Devise
10
10
  #
11
11
  # * +omniauth_providers+: Which providers are available to this model. It expects an array:
12
12
  #
13
- # devise_for :database_authenticatable, :omniauthable, :omniauth_providers => [:twitter]
13
+ # devise_for :database_authenticatable, :omniauthable, omniauth_providers: [:twitter]
14
14
  #
15
15
  module Omniauthable
16
16
  extend ActiveSupport::Concern
@@ -45,14 +45,10 @@ module Devise
45
45
  # Resets reset password token and send reset password instructions by email.
46
46
  # Returns the token sent in the e-mail.
47
47
  def send_reset_password_instructions
48
- raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
48
+ token = set_reset_password_token
49
+ send_reset_password_instructions_notification(token)
49
50
 
50
- self.reset_password_token = enc
51
- self.reset_password_sent_at = Time.now.utc
52
- self.save(:validate => false)
53
-
54
- send_devise_notification(:reset_password_instructions, raw, {})
55
- raw
51
+ token
56
52
  end
57
53
 
58
54
  # Checks if the reset password token sent is within the limit time.
@@ -90,7 +86,27 @@ module Devise
90
86
  def after_password_reset
91
87
  end
92
88
 
89
+ def set_reset_password_token
90
+ raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
91
+
92
+ self.reset_password_token = enc
93
+ self.reset_password_sent_at = Time.now.utc
94
+ self.save(validate: false)
95
+ raw
96
+ end
97
+
98
+ def send_reset_password_instructions_notification(token)
99
+ send_devise_notification(:reset_password_instructions, token, {})
100
+ end
101
+
93
102
  module ClassMethods
103
+ # Attempt to find a user by password reset token. If a user is found, return it
104
+ # If a user is not found, return nil
105
+ def with_reset_password_token(token)
106
+ reset_password_token = Devise.token_generator.digest(self, :reset_password_token, token)
107
+ to_adapter.find_first(reset_password_token: reset_password_token)
108
+ end
109
+
94
110
  # Attempt to find a user by its email. If a record is found, send new
95
111
  # password instructions to it. If user is not found, returns a new user
96
112
  # with an email not found error.
@@ -17,7 +17,7 @@ module Devise
17
17
  #
18
18
  # * +remember_for+: the time you want the user will be remembered without
19
19
  # asking for credentials. After this time the user will be blocked and
20
- # will have to enter his credentials again. This configuration is also
20
+ # will have to enter their credentials again. This configuration is also
21
21
  # used to calculate the expires time for the cookie created to remember
22
22
  # the user. By default remember_for is 2.weeks.
23
23
  #
@@ -50,7 +50,7 @@ module Devise
50
50
  def remember_me!(extend_period=false)
51
51
  self.remember_token = self.class.remember_token if generate_remember_token?
52
52
  self.remember_created_at = Time.now.utc if generate_remember_timestamp?(extend_period)
53
- save(:validate => false) if self.changed?
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
@@ -58,8 +58,8 @@ module Devise
58
58
  def forget_me!
59
59
  return unless persisted?
60
60
  self.remember_token = nil if respond_to?(:remember_token=)
61
- self.remember_created_at = nil
62
- save(:validate => false)
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
65
  # Remember token should be expired if expiration time not overpass now.
@@ -118,11 +118,11 @@ module Devise
118
118
  def remember_token #:nodoc:
119
119
  loop do
120
120
  token = Devise.friendly_token
121
- break token unless to_adapter.find_first({ :remember_token => token })
121
+ break token unless to_adapter.find_first({ remember_token: token })
122
122
  end
123
123
  end
124
124
 
125
- Devise::Models.config(self, :remember_for, :extend_remember_period, :rememberable_options)
125
+ Devise::Models.config(self, :remember_for, :extend_remember_period, :rememberable_options, :expire_all_remember_me_on_sign_out)
126
126
  end
127
127
  end
128
128
  end
@@ -2,9 +2,9 @@ require 'devise/hooks/timeoutable'
2
2
 
3
3
  module Devise
4
4
  module Models
5
- # Timeoutable takes care of verifyng whether a user session has already
5
+ # Timeoutable takes care of verifying whether a user session has already
6
6
  # 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
7
+ # will be asked for credentials again, it means, they will be redirected
8
8
  # to the sign in page.
9
9
  #
10
10
  # == Options
@@ -15,7 +15,7 @@ module Devise
15
15
  [:current_sign_in_at, :current_sign_in_ip, :last_sign_in_at, :last_sign_in_ip, :sign_in_count]
16
16
  end
17
17
 
18
- def update_tracked_fields!(request)
18
+ def update_tracked_fields(request)
19
19
  old_current, new_current = self.current_sign_in_at, Time.now.utc
20
20
  self.last_sign_in_at = old_current || new_current
21
21
  self.current_sign_in_at = new_current
@@ -26,8 +26,11 @@ module Devise
26
26
 
27
27
  self.sign_in_count ||= 0
28
28
  self.sign_in_count += 1
29
+ end
29
30
 
30
- save(:validate => false) or raise "Devise trackable could not save #{inspect}." \
31
+ def update_tracked_fields!(request)
32
+ update_tracked_fields(request)
33
+ save(validate: false) or raise "Devise trackable could not save #{inspect}." \
31
34
  "Please make sure a model using trackable can be saved at sign in."
32
35
  end
33
36
  end
@@ -26,13 +26,13 @@ module Devise
26
26
  assert_validations_api!(base)
27
27
 
28
28
  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?
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?
32
32
 
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
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
36
36
  end
37
37
  end
38
38