Patch #17796 » 0001-Expire-all-other-sessions-on-password-change.patch
app/controllers/application_controller.rb | ||
---|---|---|
49 | 49 |
end |
50 | 50 |
end |
51 | 51 |
|
52 |
before_filter :session_expiration, :user_setup, :check_if_login_required, :check_password_change, :set_localization |
|
52 |
before_filter :session_expiration, :user_setup, :force_logout_if_password_changed, :check_if_login_required, :check_password_change, :set_localization
|
|
53 | 53 |
|
54 | 54 |
rescue_from ::Unauthorized, :with => :deny_access |
55 | 55 |
rescue_from ::ActionView::MissingTemplate, :with => :missing_template |
... | ... | |
144 | 144 |
user |
145 | 145 |
end |
146 | 146 |
|
147 |
def force_logout_if_password_changed |
|
148 |
password_changed_at = User.current.password_changed_at || Time.at(0) |
|
149 |
# Make sure we force logout only for web browser sessions, not API calls |
|
150 |
# if the password was changed after the session creation. |
|
151 |
if session[:user_id] && password_changed_at.utc.to_i > session[:ctime].to_i |
|
152 |
reset_session |
|
153 |
set_localization |
|
154 |
flash[:error] = l(:error_session_expired) |
|
155 |
redirect_to signin_url |
|
156 |
end |
|
157 |
end |
|
158 |
|
|
147 | 159 |
def autologin_cookie_name |
148 | 160 |
Redmine::Configuration['autologin_cookie_name'].presence || 'autologin' |
149 | 161 |
end |
app/controllers/my_controller.rb | ||
---|---|---|
100 | 100 |
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation] |
101 | 101 |
@user.must_change_passwd = false |
102 | 102 |
if @user.save |
103 |
# Reset the session creation time to not log out this session on next |
|
104 |
# request due to ApplicationController#force_logout_if_password_changed |
|
105 |
session[:ctime] = Time.now.utc.to_i |
|
103 | 106 |
flash[:notice] = l(:notice_account_password_updated) |
104 | 107 |
redirect_to my_account_path |
105 | 108 |
end |
app/models/user.rb | ||
---|---|---|
279 | 279 |
def salt_password(clear_password) |
280 | 280 |
self.salt = User.generate_salt |
281 | 281 |
self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}") |
282 |
self.password_changed_at = Time.now |
|
282 | 283 |
end |
283 | 284 |
|
284 | 285 |
# Does the backend storage allow this user to change their password? |
/dev/null → db/migrate/20140903143914_add_password_changed_at_to_user.rb | ||
---|---|---|
1 |
class AddPasswordChangedAtToUser < ActiveRecord::Migration |
|
2 |
def change |
|
3 |
add_column :users, :password_changed_at, :datetime |
|
4 |
end |
|
5 |
end |
test/functional/my_controller_test.rb | ||
---|---|---|
185 | 185 |
assert User.try_to_login('jsmith', 'secret123') |
186 | 186 |
end |
187 | 187 |
|
188 |
def test_change_password_kills_other_sessions |
|
189 |
@request.session[:cdate] = (Time.now - 30.minutes).utc.to_i |
|
190 |
|
|
191 |
jsmith = User.find(2) |
|
192 |
jsmith.password_changed_at = Time.now |
|
193 |
jsmith.save! |
|
194 |
|
|
195 |
get 'account' |
|
196 |
assert_response 302 |
|
197 |
assert flash[:error].match(/Your session has expired/) |
|
198 |
end |
|
199 |
|
|
200 |
|
|
188 | 201 |
def test_change_password_should_redirect_if_user_cannot_change_its_password |
189 | 202 |
User.find(2).update_attribute(:auth_source_id, 1) |
190 | 203 |
|
191 |
- |