Skip to content

Commit 26b24ef

Browse files
authored
Fix for ignored redirect on expired passwords. (ValiMail#13)
* [PLAT-484] Fix skip_current_devise_controller? to consider current class ancestors. * [PLAT-484] Fix skip_current_controller? to consider current class ancestors. * [PLAT-484] Update test databases. * [PLAT-484] Fix require_no_authentication comments for clarity. * [PLAT-484] Prevent redirect loops for DeviseInvitable. * [PLAT-484] Calculate password freshness from updated_at instead of created_at. If password_previously_used_count = 1, then the previous password will be updated, not created. * [PLAT-484] Remove deprecated Gemfile.lock.ci files. * [PLAT-484] Update gitignore to ignore *.gemfile.lock files. * [PLAT-484] Update specs for revised password freshness algo. * [PLAT-484] Update test databases. * [PLAT-484] Bump version to 1.0.4.
1 parent 5200fc2 commit 26b24ef

15 files changed

+27
-553
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ Gemfile.lock
5252
.ruby-version
5353
.ruby-gemset
5454

55+
# Ignore gemfiles/*.gemfile.lock files
56+
/gemfiles/*.gemfile.lock
57+
5558
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
5659
.rvmrc
5760

Gemfile.lock.ci

Lines changed: 0 additions & 87 deletions
This file was deleted.

lib/devise/secure_password/controllers/active_helpers.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,18 @@ def redirected_in_session?
1818
warden.authenticated? && warden.session['secure_password_last_controller'] == 'Devise::SessionsController'
1919
end
2020

21+
# Prevent infinite loops and allow specified controllers to bypass.
22+
# @NOTE: The ability to extend this list may be made public, in the
23+
# future if that functionality is needed.
2124
def skip_current_controller?
2225
exclusion_list = [
2326
'Devise::SessionsController',
2427
'Devise::PasswordsWithPolicyController#edit',
25-
'Devise::PasswordsWithPolicyController#update'
28+
'Devise::PasswordsWithPolicyController#update',
29+
'DeviseInvitable::RegistrationsController#edit',
30+
'DeviseInvitable::RegistrationsController#update'
2631
]
27-
exclusion_list.select { |e| e == "#{self.class.name}#" + action_name || e == self.class.name.to_s }.empty?
32+
!(exclusion_list.include?("#{self.class.name}#" + action_name) || (exclusion_list & self.class.ancestors.map(&:to_s)).any?)
2833
end
2934

3035
def error_string_for_password_expired

lib/devise/secure_password/controllers/devise_helpers.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ class ::DeviseController
1010

1111
protected
1212

13-
# Override the devise require_no_authentication before callback so users
14-
# have to prevent authenticated users with expired passwords from
15-
# escaping to other pages without first updating their passwords.
13+
# Override the devise require_no_authentication before callback to
14+
# prevent authenticated users with expired passwords from escaping to
15+
# other pages without first updating their passwords.
1616
def require_no_authentication
1717
return if check_password_expired_and_redirect!
1818

@@ -42,11 +42,14 @@ def save_controller_state
4242
warden.session(scope_name)[:secure_last_action] = action_name
4343
end
4444

45+
# Prevent infinite loops and allow specified controllers to bypass.
46+
# @NOTE: The ability to extend this list may be made public, in the
47+
# future if that functionality is needed.
4548
def skip_current_devise_controller?
4649
exclusion_list = [
4750
'Devise::SessionsController'
4851
]
49-
exclusion_list.select { |e| e == "#{self.class.name}#" + action_name || e == self.class.name.to_s }.empty?
52+
!(exclusion_list.include?("#{self.class.name}#" + action_name) || (exclusion_list & self.class.ancestors.map(&:to_s)).any?)
5053
end
5154

5255
def error_string_for_password_expired

lib/devise/secure_password/models/previous_password.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ class PreviousPassword < ::ActiveRecord::Base
99
validates :encrypted_password, presence: true
1010

1111
def fresh?(minimum_age_duration, now = ::Time.zone.now)
12-
now <= (created_at + minimum_age_duration)
12+
now <= (updated_at + minimum_age_duration)
1313
end
1414

1515
def stale?(maximum_age_duration, now = ::Time.zone.now)
16-
now > (created_at + maximum_age_duration)
16+
now > (updated_at + maximum_age_duration)
1717
end
1818
end
1919
end

lib/devise/secure_password/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module Devise
22
module SecurePassword
3-
VERSION = '1.0.3'.freeze
3+
VERSION = '1.0.4'.freeze
44
end
55
end

spec/feature/user_changes_password_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
before do
4646
user.save
4747
last_password = user.previous_passwords.unscoped.last
48-
last_password.created_at = Time.zone.now - 2.days
48+
last_password.created_at = last_password.updated_at = Time.zone.now - 2.days
4949
last_password.save
5050
login_as(user, scope: :user)
5151
visit '/users/change_password/edit'

spec/feature/user_logs_in_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
before do
1515
user.save
1616
last_password = user.previous_passwords.unscoped.last
17-
last_password.created_at = Time.zone.now - User.password_maximum_age
17+
last_password.created_at = last_password.updated_at = Time.zone.now - User.password_maximum_age
1818
last_password.save
1919
visit '/users/sign_in'
2020
end

spec/models/password_disallows_frequent_changes_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
user.save
6363
# reset its previous_password record to one day before
6464
last_password = user.previous_passwords.unscoped.last
65-
last_password.created_at = Time.zone.now - Isolated::UserFrequentChanges.password_minimum_age
65+
last_password.created_at = last_password.updated_at = Time.zone.now - Isolated::UserFrequentChanges.password_minimum_age
6666
last_password.save
6767
# bypass frequent_reuse validator by changing password
6868
user.password = user.password_confirmation = user.password + 'Z'
@@ -89,7 +89,7 @@
8989
user.save
9090
# reset its previous_password record one day past minimum
9191
last_password = user.previous_passwords.unscoped.last
92-
last_password.created_at = Time.zone.now - Isolated::UserFrequentChanges.password_minimum_age
92+
last_password.created_at = last_password.updated_at = Time.zone.now - Isolated::UserFrequentChanges.password_minimum_age
9393
last_password.save
9494
end
9595

spec/models/password_requires_regular_updates_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
user.save
4242
# reset its previous_password record to one day past maximum
4343
last_password = user.previous_passwords.unscoped.last
44-
last_password.created_at = Time.zone.now - Isolated::UserRegularUpdates.password_maximum_age
44+
last_password.created_at = last_password.updated_at = Time.zone.now - Isolated::UserRegularUpdates.password_maximum_age
4545
last_password.save
4646
end
4747

spec/models/previous_password_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171

7272
context 'when a password is not recent' do
7373
it 'returns false' do
74-
previous_password.created_at = Time.zone.now - 1.day
74+
previous_password.created_at = previous_password.updated_at = Time.zone.now - 1.day
7575
expect(previous_password.fresh?(1.day)).to be false
7676
end
7777
end
@@ -92,7 +92,7 @@
9292

9393
context 'when a password is old' do
9494
it 'returns true' do
95-
previous_password.created_at = Time.zone.now - 1.day
95+
previous_password.created_at = previous_password.updated_at = Time.zone.now - 1.day
9696
expect(previous_password.stale?(1.day)).to be true
9797
end
9898
end

0 commit comments

Comments
 (0)