devise 3.4.1 → 3.5.10

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 (116) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +28 -19
  3. data/CHANGELOG.md +193 -104
  4. data/CODE_OF_CONDUCT.md +22 -0
  5. data/CONTRIBUTING.md +2 -0
  6. data/Gemfile +3 -2
  7. data/Gemfile.lock +90 -95
  8. data/MIT-LICENSE +1 -1
  9. data/README.md +55 -34
  10. data/Rakefile +2 -1
  11. data/app/controllers/devise/confirmations_controller.rb +4 -0
  12. data/app/controllers/devise/omniauth_callbacks_controller.rb +4 -0
  13. data/app/controllers/devise/passwords_controller.rb +14 -4
  14. data/app/controllers/devise/registrations_controller.rb +10 -11
  15. data/app/controllers/devise/sessions_controller.rb +7 -2
  16. data/app/controllers/devise/unlocks_controller.rb +3 -0
  17. data/app/controllers/devise_controller.rb +34 -18
  18. data/app/mailers/devise/mailer.rb +4 -0
  19. data/app/views/devise/confirmations/new.html.erb +1 -1
  20. data/app/views/devise/mailer/password_change.html.erb +3 -0
  21. data/app/views/devise/passwords/edit.html.erb +3 -0
  22. data/app/views/devise/registrations/new.html.erb +1 -1
  23. data/app/views/devise/shared/_links.html.erb +1 -1
  24. data/config/locales/en.yml +2 -0
  25. data/devise.gemspec +0 -2
  26. data/gemfiles/Gemfile.rails-3.2-stable.lock +52 -49
  27. data/gemfiles/Gemfile.rails-4.0-stable +1 -0
  28. data/gemfiles/Gemfile.rails-4.0-stable.lock +61 -60
  29. data/gemfiles/Gemfile.rails-4.1-stable +1 -0
  30. data/gemfiles/Gemfile.rails-4.1-stable.lock +66 -65
  31. data/gemfiles/Gemfile.rails-4.2-stable +30 -0
  32. data/gemfiles/Gemfile.rails-4.2-stable.lock +193 -0
  33. data/lib/devise/controllers/helpers.rb +12 -6
  34. data/lib/devise/controllers/rememberable.rb +9 -2
  35. data/lib/devise/controllers/sign_in_out.rb +2 -8
  36. data/lib/devise/controllers/store_location.rb +3 -1
  37. data/lib/devise/controllers/url_helpers.rb +7 -9
  38. data/lib/devise/encryptor.rb +22 -0
  39. data/lib/devise/failure_app.rb +48 -13
  40. data/lib/devise/hooks/timeoutable.rb +5 -7
  41. data/lib/devise/mapping.rb +1 -0
  42. data/lib/devise/models/authenticatable.rb +20 -26
  43. data/lib/devise/models/confirmable.rb +51 -17
  44. data/lib/devise/models/database_authenticatable.rb +17 -11
  45. data/lib/devise/models/lockable.rb +5 -1
  46. data/lib/devise/models/recoverable.rb +23 -15
  47. data/lib/devise/models/rememberable.rb +56 -22
  48. data/lib/devise/models/timeoutable.rb +0 -6
  49. data/lib/devise/models/trackable.rb +1 -2
  50. data/lib/devise/models/validatable.rb +3 -3
  51. data/lib/devise/models.rb +1 -1
  52. data/lib/devise/rails/routes.rb +27 -18
  53. data/lib/devise/rails.rb +1 -1
  54. data/lib/devise/strategies/authenticatable.rb +7 -4
  55. data/lib/devise/strategies/database_authenticatable.rb +1 -1
  56. data/lib/devise/strategies/rememberable.rb +13 -6
  57. data/lib/devise/test_helpers.rb +2 -2
  58. data/lib/devise/version.rb +1 -1
  59. data/lib/devise.rb +37 -36
  60. data/lib/generators/active_record/templates/migration.rb +1 -1
  61. data/lib/generators/active_record/templates/migration_existing.rb +1 -1
  62. data/lib/generators/devise/views_generator.rb +14 -3
  63. data/lib/generators/templates/controllers/README +2 -2
  64. data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +1 -1
  65. data/lib/generators/templates/controllers/registrations_controller.rb +2 -2
  66. data/lib/generators/templates/controllers/sessions_controller.rb +1 -1
  67. data/lib/generators/templates/devise.rb +17 -11
  68. data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
  69. data/lib/generators/templates/markerb/password_change.markerb +3 -0
  70. data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
  71. data/lib/generators/templates/markerb/unlock_instructions.markerb +1 -1
  72. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +1 -1
  73. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +1 -1
  74. data/test/controllers/custom_registrations_controller_test.rb +6 -1
  75. data/test/controllers/helper_methods_test.rb +21 -0
  76. data/test/controllers/helpers_test.rb +5 -0
  77. data/test/controllers/inherited_controller_i18n_messages_test.rb +51 -0
  78. data/test/controllers/internal_helpers_test.rb +4 -4
  79. data/test/controllers/load_hooks_controller_test.rb +19 -0
  80. data/test/controllers/passwords_controller_test.rb +1 -1
  81. data/test/controllers/sessions_controller_test.rb +3 -3
  82. data/test/devise_test.rb +3 -3
  83. data/test/failure_app_test.rb +40 -0
  84. data/test/generators/views_generator_test.rb +7 -0
  85. data/test/integration/database_authenticatable_test.rb +11 -0
  86. data/test/integration/omniauthable_test.rb +12 -10
  87. data/test/integration/recoverable_test.rb +13 -0
  88. data/test/integration/rememberable_test.rb +50 -3
  89. data/test/integration/timeoutable_test.rb +13 -18
  90. data/test/mailers/confirmation_instructions_test.rb +1 -1
  91. data/test/mapping_test.rb +6 -0
  92. data/test/models/confirmable_test.rb +93 -37
  93. data/test/models/database_authenticatable_test.rb +20 -0
  94. data/test/models/lockable_test.rb +29 -7
  95. data/test/models/recoverable_test.rb +62 -7
  96. data/test/models/rememberable_test.rb +68 -97
  97. data/test/models/validatable_test.rb +5 -5
  98. data/test/models_test.rb +15 -6
  99. data/test/rails_app/app/active_record/user_without_email.rb +8 -0
  100. data/test/rails_app/app/controllers/admins_controller.rb +0 -5
  101. data/test/rails_app/app/controllers/custom/registrations_controller.rb +10 -0
  102. data/test/rails_app/app/mongoid/user_without_email.rb +33 -0
  103. data/test/rails_app/config/application.rb +1 -1
  104. data/test/rails_app/config/environments/production.rb +6 -2
  105. data/test/rails_app/config/environments/test.rb +7 -2
  106. data/test/rails_app/config/initializers/devise.rb +12 -15
  107. data/test/rails_app/config/routes.rb +6 -3
  108. data/test/rails_app/lib/shared_user.rb +1 -1
  109. data/test/rails_app/lib/shared_user_without_email.rb +26 -0
  110. data/test/rails_test.rb +9 -0
  111. data/test/support/helpers.rb +4 -0
  112. data/test/support/integration.rb +2 -2
  113. data/test/test_helpers_test.rb +22 -7
  114. data/test/test_models.rb +2 -2
  115. data/test/time_helpers.rb +137 -0
  116. metadata +26 -4
@@ -8,15 +8,13 @@ module Devise
8
8
  # Recoverable adds the following options to devise_for:
9
9
  #
10
10
  # * +reset_password_keys+: the keys you want to use when recovering the password for an account
11
+ # * +reset_password_within+: the time period within which the password must be reset or the token expires.
12
+ # * +sign_in_after_reset_password+: whether or not to sign in the user automatically after a password reset.
11
13
  #
12
14
  # == Examples
13
15
  #
14
16
  # # resets the user password and save the record, true if valid passwords are given, otherwise false
15
- # User.find(1).reset_password!('password123', 'password123')
16
- #
17
- # # only resets the user password, without saving the record
18
- # user = User.find(1)
19
- # user.reset_password('password123', 'password123')
17
+ # User.find(1).reset_password('password123', 'password123')
20
18
  #
21
19
  # # creates a new token and send it with instructions about how to reset the password
22
20
  # User.find(1).send_reset_password_instructions
@@ -28,20 +26,33 @@ module Devise
28
26
  [:reset_password_sent_at, :reset_password_token]
29
27
  end
30
28
 
29
+ included do
30
+ before_update do
31
+ if (respond_to?(:email_changed?) && email_changed?) || encrypted_password_changed?
32
+ clear_reset_password_token
33
+ end
34
+ end
35
+ end
36
+
31
37
  # Update password saving the record and clearing token. Returns true if
32
38
  # the passwords are valid and the record was saved, false otherwise.
33
- def reset_password!(new_password, new_password_confirmation)
39
+ def reset_password(new_password, new_password_confirmation)
34
40
  self.password = new_password
35
41
  self.password_confirmation = new_password_confirmation
36
42
 
37
- if valid?
38
- clear_reset_password_token
43
+ if respond_to?(:after_password_reset) && valid?
44
+ ActiveSupport::Deprecation.warn "after_password_reset is deprecated"
39
45
  after_password_reset
40
46
  end
41
47
 
42
48
  save
43
49
  end
44
50
 
51
+ def reset_password!(new_password, new_password_confirmation)
52
+ ActiveSupport::Deprecation.warn "reset_password! is deprecated in favor of reset_password"
53
+ reset_password(new_password, new_password_confirmation)
54
+ end
55
+
45
56
  # Resets reset password token and send reset password instructions by email.
46
57
  # Returns the token sent in the e-mail.
47
58
  def send_reset_password_instructions
@@ -72,7 +83,7 @@ module Devise
72
83
  # reset_password_period_valid? # will always return false
73
84
  #
74
85
  def reset_password_period_valid?
75
- reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago
86
+ reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago.utc
76
87
  end
77
88
 
78
89
  protected
@@ -83,9 +94,6 @@ module Devise
83
94
  self.reset_password_sent_at = nil
84
95
  end
85
96
 
86
- def after_password_reset
87
- end
88
-
89
97
  def set_reset_password_token
90
98
  raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
91
99
 
@@ -130,17 +138,17 @@ module Devise
130
138
 
131
139
  if recoverable.persisted?
132
140
  if recoverable.reset_password_period_valid?
133
- recoverable.reset_password!(attributes[:password], attributes[:password_confirmation])
141
+ recoverable.reset_password(attributes[:password], attributes[:password_confirmation])
134
142
  else
135
143
  recoverable.errors.add(:reset_password_token, :expired)
136
144
  end
137
145
  end
138
146
 
139
- recoverable.reset_password_token = original_token
147
+ recoverable.reset_password_token = original_token if recoverable.reset_password_token.present?
140
148
  recoverable
141
149
  end
142
150
 
143
- Devise::Models.config(self, :reset_password_keys, :reset_password_within)
151
+ Devise::Models.config(self, :reset_password_keys, :reset_password_within, :sign_in_after_reset_password)
144
152
  end
145
153
  end
146
154
  end
@@ -39,17 +39,17 @@ module Devise
39
39
  module Rememberable
40
40
  extend ActiveSupport::Concern
41
41
 
42
- attr_accessor :remember_me, :extend_remember_period
42
+ attr_accessor :remember_me
43
43
 
44
44
  def self.required_fields(klass)
45
45
  [:remember_created_at]
46
46
  end
47
47
 
48
- # Generate a new remember token and save the record without validations
49
- # 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)
48
+ # TODO: We were used to receive a extend period argument but we no longer do.
49
+ # Remove this for Devise 4.0.
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,25 +57,28 @@ 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
65
  # Remember token should be expired if expiration time not overpass now.
66
66
  def remember_expired?
67
- remember_created_at.nil? || (remember_expires_at <= Time.now.utc)
67
+ remember_created_at.nil?
68
68
  end
69
69
 
70
- # Remember token expires at created time + remember_for configuration
71
70
  def remember_expires_at
72
- remember_created_at + self.class.remember_for
71
+ self.class.remember_for.from_now
72
+ end
73
+
74
+ def extend_remember_period
75
+ self.class.extend_remember_period
73
76
  end
74
77
 
75
78
  def rememberable_value
76
79
  if respond_to?(:remember_token)
77
80
  remember_token
78
- elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt)
81
+ elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt.presence)
79
82
  salt
80
83
  else
81
84
  raise "authenticable_salt returned nil for the #{self.class.name} model. " \
@@ -89,29 +92,60 @@ module Devise
89
92
  self.class.rememberable_options
90
93
  end
91
94
 
92
- protected
95
+ # A callback initiated after successfully being remembered. This can be
96
+ # used to insert your own logic that is only run after the user is
97
+ # remembered.
98
+ #
99
+ # Example:
100
+ #
101
+ # def after_remembered
102
+ # self.update_attribute(:invite_code, nil)
103
+ # end
104
+ #
105
+ def after_remembered
106
+ end
107
+
108
+ def remember_me?(token, generated_at)
109
+ # TODO: Normalize the JSON type coercion along with the Timeoutable hook
110
+ # in a single place https://github.com/plataformatec/devise/blob/ffe9d6d406e79108cf32a2c6a1d0b3828849c40b/lib/devise/hooks/timeoutable.rb#L14-L18
111
+ if generated_at.is_a?(String)
112
+ generated_at = time_from_json(generated_at)
113
+ end
93
114
 
94
- def generate_remember_token? #:nodoc:
95
- respond_to?(:remember_token) && remember_expired?
115
+ # The token is only valid if:
116
+ # 1. we have a date
117
+ # 2. the current time does not pass the expiry period
118
+ # 3. the record has a remember_created_at date
119
+ # 4. the token date is bigger than the remember_created_at
120
+ # 5. the token matches
121
+ generated_at.is_a?(Time) &&
122
+ (self.class.remember_for.ago < generated_at) &&
123
+ (generated_at > (remember_created_at || Time.now).utc) &&
124
+ Devise.secure_compare(rememberable_value, token)
96
125
  end
97
126
 
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?
127
+ private
128
+
129
+ def time_from_json(value)
130
+ if value =~ /\A\d+\.\d+\Z/
131
+ Time.at(value.to_f)
132
+ else
133
+ Time.parse(value) rescue nil
134
+ end
102
135
  end
103
136
 
104
137
  module ClassMethods
105
138
  # Create the cookie key using the record id and remember_token
106
139
  def serialize_into_cookie(record)
107
- [record.to_key, record.rememberable_value]
140
+ [record.to_key, record.rememberable_value, Time.now.utc.to_f.to_s]
108
141
  end
109
142
 
110
143
  # Recreate the user based on the stored cookie
111
- def serialize_from_cookie(id, remember_token)
144
+ def serialize_from_cookie(*args)
145
+ id, token, generated_at = *args
146
+
112
147
  record = to_adapter.get(id)
113
- record if record && !record.remember_expired? &&
114
- Devise.secure_compare(record.rememberable_value, remember_token)
148
+ record if record && record.remember_me?(token, generated_at)
115
149
  end
116
150
 
117
151
  # Generate a token checking if one does not already exist in the database.
@@ -26,7 +26,6 @@ module Devise
26
26
 
27
27
  # Checks whether the user session has expired based on configured time.
28
28
  def timedout?(last_access)
29
- return false if remember_exists_and_not_expired?
30
29
  !timeout_in.nil? && last_access && last_access <= timeout_in.ago
31
30
  end
32
31
 
@@ -36,11 +35,6 @@ module Devise
36
35
 
37
36
  private
38
37
 
39
- def remember_exists_and_not_expired?
40
- return false unless respond_to?(:remember_created_at) && respond_to?(:remember_expired?)
41
- remember_created_at && !remember_expired?
42
- end
43
-
44
38
  module ClassMethods
45
39
  Devise::Models.config(self, :timeout_in)
46
40
  end
@@ -30,8 +30,7 @@ module Devise
30
30
 
31
31
  def update_tracked_fields!(request)
32
32
  update_tracked_fields(request)
33
- save(validate: false) or raise "Devise trackable could not save #{inspect}." \
34
- "Please make sure a model using trackable can be saved at sign in."
33
+ save(validate: false)
35
34
  end
36
35
  end
37
36
  end
@@ -10,12 +10,12 @@ module Devise
10
10
  # Validatable adds the following options to devise_for:
11
11
  #
12
12
  # * +email_regexp+: the regular expression used to validate e-mails;
13
- # * +password_length+: a range expressing password length. Defaults to 8..128.
13
+ # * +password_length+: a range expressing password length. Defaults to 8..72.
14
14
  #
15
15
  module Validatable
16
16
  # 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
17
+ VALIDATIONS = [:validates_presence_of, :validates_uniqueness_of, :validates_format_of,
18
+ :validates_confirmation_of, :validates_length_of].freeze
19
19
 
20
20
  def self.required_fields(klass)
21
21
  []
data/lib/devise/models.rb CHANGED
@@ -12,7 +12,7 @@ module Devise
12
12
 
13
13
  # Creates configuration values for Devise and for the given module.
14
14
  #
15
- # Devise::Models.config(Devise::Authenticatable, :stretches, 10)
15
+ # Devise::Models.config(Devise::DatabaseAuthenticatable, :stretches)
16
16
  #
17
17
  # The line above creates:
18
18
  #
@@ -94,10 +94,24 @@ module ActionDispatch::Routing
94
94
  #
95
95
  # devise_for :users, path: 'accounts'
96
96
  #
97
- # * singular: setup the singular name for the given resource. This is used as the instance variable
98
- # name in controller, as the name in routes and the scope given to warden.
97
+ # * singular: setup the singular name for the given resource. This is used as the helper methods
98
+ # names in controller ("authenticate_#{singular}!", "#{singular}_signed_in?", "current_#{singular}"
99
+ # and "#{singular}_session"), as the scope name in routes and as the scope given to warden.
99
100
  #
100
- # devise_for :users, singular: :user
101
+ # devise_for :admins, singular: :manager
102
+ #
103
+ # devise_scope :manager do
104
+ # ...
105
+ # end
106
+ #
107
+ # class ManagerController < ApplicationController
108
+ # before_filter authenticate_manager!
109
+ #
110
+ # def show
111
+ # @manager = current_manager
112
+ # ...
113
+ # end
114
+ # end
101
115
  #
102
116
  # * path_names: configure different path names to overwrite defaults :sign_in, :sign_out, :sign_up,
103
117
  # :password, :confirmation, :unlock.
@@ -119,7 +133,7 @@ module ActionDispatch::Routing
119
133
  # * sign_out_via: the HTTP method(s) accepted for the :sign_out action (default: :get),
120
134
  # if you wish to restrict this to accept only :post or :delete requests you should do:
121
135
  #
122
- # devise_for :users, sign_out_via: [ :post, :delete ]
136
+ # devise_for :users, sign_out_via: [:post, :delete]
123
137
  #
124
138
  # You need to make sure that your sign_out controls trigger a request with a matching HTTP method.
125
139
  #
@@ -402,21 +416,16 @@ module ActionDispatch::Routing
402
416
  def devise_omniauth_callback(mapping, controllers) #:nodoc:
403
417
  if mapping.fullpath =~ /:[a-zA-Z_]/
404
418
  raise <<-ERROR
405
- Devise does not support scoping omniauth callbacks under a dynamic segment
419
+ Devise does not support scoping OmniAuth callbacks under a dynamic segment
406
420
  and you have set #{mapping.fullpath.inspect}. You can work around by passing
407
- `skip: :omniauth_callbacks` and manually defining the routes. Here is an example:
408
-
409
- match "/users/auth/:provider",
410
- constraints: { provider: /google|facebook/ },
411
- to: "devise/omniauth_callbacks#passthru",
412
- as: :omniauth_authorize,
413
- via: [:get, :post]
414
-
415
- match "/users/auth/:action/callback",
416
- constraints: { action: /google|facebook/ },
417
- to: "devise/omniauth_callbacks",
418
- as: :omniauth_callback,
419
- via: [:get, :post]
421
+ `skip: :omniauth_callbacks` to the `devise_for` call and extract omniauth
422
+ options to another `devise_for` call outside the scope. Here is an example:
423
+
424
+ devise_for :users, only: :omniauth_callbacks, controllers: {omniauth_callbacks: 'users/omniauth_callbacks'}
425
+
426
+ scope '/(:locale)', locale: /ru|en/ do
427
+ devise_for :users, skip: :omniauth_callbacks
428
+ end
420
429
  ERROR
421
430
  end
422
431
 
data/lib/devise/rails.rb CHANGED
@@ -17,7 +17,7 @@ module Devise
17
17
  Devise.include_helpers(Devise::Controllers)
18
18
  end
19
19
 
20
- initializer "devise.omniauth" do |app|
20
+ initializer "devise.omniauth", after: :load_config_initializers, before: :build_middleware_stack do |app|
21
21
  Devise.omniauth_configs.each do |provider, config|
22
22
  app.middleware.use config.strategy_class, *config.args do |strategy|
23
23
  config.strategy = strategy
@@ -27,7 +27,7 @@ module Devise
27
27
 
28
28
  # Receives a resource and check if it is valid by calling valid_for_authentication?
29
29
  # An optional block that will be triggered while validating can be optionally
30
- # given as parameter. Check Devise::Models::Authenticable.valid_for_authentication?
30
+ # given as parameter. Check Devise::Models::Authenticatable.valid_for_authentication?
31
31
  # for more information.
32
32
  #
33
33
  # In case the resource can't be validated, it will fail with the given
@@ -57,7 +57,7 @@ module Devise
57
57
 
58
58
  # Check if this is a valid strategy for http authentication by:
59
59
  #
60
- # * Validating if the model allows params authentication;
60
+ # * Validating if the model allows http authentication;
61
61
  # * If any of the authorization headers were sent;
62
62
  # * If all authentication keys are present;
63
63
  #
@@ -108,14 +108,17 @@ module Devise
108
108
  params_auth_hash.is_a?(Hash)
109
109
  end
110
110
 
111
- # Check if password is present.
111
+ # Note: unlike `Model.valid_password?`, this method does not actually
112
+ # ensure that the password in the params matches the password stored in
113
+ # the database. It only checks if the password is *present*. Do not rely
114
+ # on this method for validating that a given password is correct.
112
115
  def valid_password?
113
116
  password.present?
114
117
  end
115
118
 
116
119
  # Helper to decode credentials from HTTP.
117
120
  def decode_credentials
118
- return [] unless request.authorization && request.authorization =~ /^Basic (.*)/m
121
+ return [] unless request.authorization && request.authorization =~ /^Basic (.*)/mi
119
122
  Base64.decode64($1).split(/:/, 2)
120
123
  end
121
124
 
@@ -5,7 +5,7 @@ module Devise
5
5
  # Default strategy for signing in a user, based on their email and password in the database.
6
6
  class DatabaseAuthenticatable < Authenticatable
7
7
  def authenticate!
8
- resource = valid_password? && mapping.to.find_for_database_authentication(authentication_hash)
8
+ resource = password.present? && mapping.to.find_for_database_authentication(authentication_hash)
9
9
  encrypted = false
10
10
 
11
11
  if validate(resource){ encrypted = true; resource.valid_password?(password) }
@@ -25,18 +25,25 @@ module Devise
25
25
  end
26
26
 
27
27
  if validate(resource)
28
- remember_me(resource)
29
- extend_remember_me_period(resource)
28
+ remember_me(resource) if extend_remember_me?(resource)
29
+ resource.after_remembered
30
30
  success!(resource)
31
31
  end
32
32
  end
33
33
 
34
+ # No need to clean up the CSRF when using rememberable.
35
+ # In fact, cleaning it up here would be a bug because
36
+ # rememberable is triggered on GET requests which means
37
+ # we would render a page on first access with all csrf
38
+ # tokens expired.
39
+ def clean_up_csrf?
40
+ false
41
+ end
42
+
34
43
  private
35
44
 
36
- def extend_remember_me_period(resource)
37
- if resource.respond_to?(:extend_remember_period=)
38
- resource.extend_remember_period = mapping.to.extend_remember_period
39
- end
45
+ def extend_remember_me?(resource)
46
+ resource.respond_to?(:extend_remember_period) && resource.extend_remember_period
40
47
  end
41
48
 
42
49
  def remember_me?
@@ -26,11 +26,11 @@ module Devise
26
26
 
27
27
  # Quick access to Warden::Proxy.
28
28
  def warden #:nodoc:
29
- @warden ||= begin
29
+ @request.env['warden'] ||= begin
30
30
  manager = Warden::Manager.new(nil) do |config|
31
31
  config.merge! Devise.warden_config
32
32
  end
33
- @request.env['warden'] = Warden::Proxy.new(@request.env, manager)
33
+ Warden::Proxy.new(@request.env, manager)
34
34
  end
35
35
  end
36
36
 
@@ -1,3 +1,3 @@
1
1
  module Devise
2
- VERSION = "3.4.1".freeze
2
+ VERSION = "3.5.10".freeze
3
3
  end
data/lib/devise.rb CHANGED
@@ -8,6 +8,7 @@ require 'responders'
8
8
 
9
9
  module Devise
10
10
  autoload :Delegator, 'devise/delegator'
11
+ autoload :Encryptor, 'devise/encryptor'
11
12
  autoload :FailureApp, 'devise/failure_app'
12
13
  autoload :OmniAuth, 'devise/omniauth'
13
14
  autoload :ParameterFilter, 'devise/parameter_filter'
@@ -57,22 +58,6 @@ module Devise
57
58
  mattr_accessor :secret_key
58
59
  @@secret_key = nil
59
60
 
60
- [ :allow_insecure_token_lookup,
61
- :allow_insecure_sign_in_after_confirmation,
62
- :token_authentication_key ].each do |method|
63
- class_eval <<-RUBY
64
- def self.#{method}
65
- ActiveSupport::Deprecation.warn "Devise.#{method} is deprecated " \
66
- "and has no effect"
67
- end
68
-
69
- def self.#{method}=(val)
70
- ActiveSupport::Deprecation.warn "Devise.#{method}= is deprecated " \
71
- "and has no effect"
72
- end
73
- RUBY
74
- end
75
-
76
61
  # Custom domain or key for cookies. Not set by default
77
62
  mattr_accessor :rememberable_options
78
63
  @@rememberable_options = {}
@@ -87,7 +72,7 @@ module Devise
87
72
 
88
73
  # Keys used when authenticating a user.
89
74
  mattr_accessor :authentication_keys
90
- @@authentication_keys = [ :email ]
75
+ @@authentication_keys = [:email]
91
76
 
92
77
  # Request keys used when authenticating a user.
93
78
  mattr_accessor :request_keys
@@ -95,7 +80,7 @@ module Devise
95
80
 
96
81
  # Keys that should be case-insensitive.
97
82
  mattr_accessor :case_insensitive_keys
98
- @@case_insensitive_keys = [ :email ]
83
+ @@case_insensitive_keys = [:email]
99
84
 
100
85
  # Keys that should have whitespace stripped.
101
86
  mattr_accessor :strip_whitespace_keys
@@ -121,7 +106,7 @@ module Devise
121
106
  # an one (and only one) @ exists in the given string. This is mainly
122
107
  # to give user feedback and not to assert the e-mail validity.
123
108
  mattr_accessor :email_regexp
124
- @@email_regexp = /\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/
109
+ @@email_regexp = /\A[^@\s]+@([^@\s]+\.)+[^@\W]+\z/
125
110
 
126
111
  # Range validation for password length
127
112
  mattr_accessor :password_length
@@ -150,7 +135,7 @@ module Devise
150
135
 
151
136
  # Defines which key will be used when confirming an account.
152
137
  mattr_accessor :confirmation_keys
153
- @@confirmation_keys = [ :email ]
138
+ @@confirmation_keys = [:email]
154
139
 
155
140
  # Defines if email should be reconfirmable.
156
141
  # False by default for backwards compatibility.
@@ -161,14 +146,14 @@ module Devise
161
146
  mattr_accessor :timeout_in
162
147
  @@timeout_in = 30.minutes
163
148
 
164
- # Authentication token expiration on timeout
165
- mattr_accessor :expire_auth_token_on_timeout
166
- @@expire_auth_token_on_timeout = false
167
-
168
149
  # Used to encrypt password. Please generate one with rake secret.
169
150
  mattr_accessor :pepper
170
151
  @@pepper = nil
171
152
 
153
+ # Used to enable sending notification to user when their password is changed
154
+ mattr_accessor :send_password_change_notification
155
+ @@send_password_change_notification = false
156
+
172
157
  # Scoped views. Since it relies on fallbacks to render default views, it's
173
158
  # turned off by default.
174
159
  mattr_accessor :scoped_views
@@ -181,7 +166,7 @@ module Devise
181
166
 
182
167
  # Defines which key will be used when locking and unlocking an account
183
168
  mattr_accessor :unlock_keys
184
- @@unlock_keys = [ :email ]
169
+ @@unlock_keys = [:email]
185
170
 
186
171
  # Defines which strategy can be used to unlock an account.
187
172
  # Values: :email, :time, :both
@@ -198,12 +183,16 @@ module Devise
198
183
 
199
184
  # Defines which key will be used when recovering the password for an account
200
185
  mattr_accessor :reset_password_keys
201
- @@reset_password_keys = [ :email ]
186
+ @@reset_password_keys = [:email]
202
187
 
203
188
  # Time interval you can reset your password with a reset password key
204
189
  mattr_accessor :reset_password_within
205
190
  @@reset_password_within = 6.hours
206
191
 
192
+ # When set to false, resetting a password does not automatically sign in a user
193
+ mattr_accessor :sign_in_after_reset_password
194
+ @@sign_in_after_reset_password = true
195
+
207
196
  # The default scope which is used by warden.
208
197
  mattr_accessor :default_scope
209
198
  @@default_scope = nil
@@ -246,7 +235,7 @@ module Devise
246
235
  mattr_accessor :router_name
247
236
  @@router_name = nil
248
237
 
249
- # Set the omniauth path prefix so it can be overridden when
238
+ # Set the OmniAuth path prefix so it can be overridden when
250
239
  # Devise is used in a mountable engine
251
240
  mattr_accessor :omniauth_path_prefix
252
241
  @@omniauth_path_prefix = nil
@@ -261,7 +250,7 @@ module Devise
261
250
  mattr_reader :mappings
262
251
  @@mappings = ActiveSupport::OrderedHash.new
263
252
 
264
- # Omniauth configurations.
253
+ # OmniAuth configurations.
265
254
  mattr_reader :omniauth_configs
266
255
  @@omniauth_configs = ActiveSupport::OrderedHash.new
267
256
 
@@ -340,7 +329,12 @@ module Devise
340
329
  mapping
341
330
  end
342
331
 
343
- # Make Devise aware of an 3rd party Devise-module (like invitable). For convenience.
332
+ # Register available devise modules. For the standard modules that Devise provides, this method is
333
+ # called from lib/devise/modules.rb. Third-party modules need to be added explicitly using this method.
334
+ #
335
+ # Note that adding a module using this method does not cause it to be used in the authentication
336
+ # process. That requires that the module be listed in the arguments passed to the 'devise' method
337
+ # in the model class definition.
344
338
  #
345
339
  # == Options:
346
340
  #
@@ -348,6 +342,7 @@ module Devise
348
342
  # +controller+ - Symbol representing the name of an existing or custom *controller* for this module.
349
343
  # +route+ - Symbol representing the named *route* helper for this module.
350
344
  # +strategy+ - Symbol representing if this module got a custom *strategy*.
345
+ # +insert_at+ - Integer representing the order in which this module's model will be included
351
346
  #
352
347
  # All values, except :model, accept also a boolean and will have the same name as the given module
353
348
  # name.
@@ -357,10 +352,12 @@ module Devise
357
352
  # Devise.add_module(:party_module)
358
353
  # Devise.add_module(:party_module, strategy: true, controller: :sessions)
359
354
  # Devise.add_module(:party_module, model: 'party_module/model')
355
+ # Devise.add_module(:party_module, insert_at: 0)
360
356
  #
361
357
  def self.add_module(module_name, options = {})
362
- ALL << module_name
363
- options.assert_valid_keys(:strategy, :model, :controller, :route, :no_input)
358
+ options.assert_valid_keys(:strategy, :model, :controller, :route, :no_input, :insert_at)
359
+
360
+ ALL.insert (options[:insert_at] || -1), module_name
364
361
 
365
362
  if strategy = options[:strategy]
366
363
  strategy = (strategy == true ? module_name : strategy)
@@ -417,7 +414,7 @@ module Devise
417
414
  @@warden_config_blocks << block
418
415
  end
419
416
 
420
- # Specify an omniauth provider.
417
+ # Specify an OmniAuth provider.
421
418
  #
422
419
  # config.omniauth :github, APP_ID, APP_SECRET
423
420
  #
@@ -445,8 +442,8 @@ module Devise
445
442
  Devise::Controllers::UrlHelpers.generate_helpers!
446
443
  end
447
444
 
448
- # A method used internally to setup warden manager from the Rails initialize
449
- # block.
445
+ # A method used internally to complete the setup of warden manager after routes are loaded.
446
+ # See lib/devise/rails/routes.rb - ActionDispatch::Routing::RouteSet#finalize_with_devise!
450
447
  def self.configure_warden! #:nodoc:
451
448
  @@warden_configured ||= begin
452
449
  warden_config.failure_app = Devise::Delegator.new
@@ -474,8 +471,12 @@ module Devise
474
471
  end
475
472
 
476
473
  # Generate a friendly string randomly to be used as token.
477
- def self.friendly_token
478
- SecureRandom.urlsafe_base64(15).tr('lIO0', 'sxyz')
474
+ # By default, length is 20 characters.
475
+ def self.friendly_token(length = 20)
476
+ # To calculate real characters, we must perform this operation.
477
+ # See SecureRandom.urlsafe_base64
478
+ rlength = (length * 3) / 4
479
+ SecureRandom.urlsafe_base64(rlength).tr('lIO0', 'sxyz')
479
480
  end
480
481
 
481
482
  # constant-time comparison algorithm to prevent timing attacks
@@ -7,7 +7,7 @@ class DeviseCreate<%= table_name.camelize %> < ActiveRecord::Migration
7
7
  t.<%= attribute.type %> :<%= attribute.name %>
8
8
  <% end -%>
9
9
 
10
- t.timestamps
10
+ t.timestamps null: false
11
11
  end
12
12
 
13
13
  add_index :<%= table_name %>, :email, unique: true