##// END OF EJS Templates
Expire other sessions on password change (#17796)....
Jean-Baptiste Barth -
r13049:b519aba63ee0
parent child
Show More
@@ -0,0 +1,5
1 class AddPasswordChangedAtToUser < ActiveRecord::Migration
2 def change
3 add_column :users, :passwd_changed_on, :datetime
4 end
5 end
@@ -49,7 +49,7 class ApplicationController < ActionController::Base
49 end
49 end
50 end
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 rescue_from ::Unauthorized, :with => :deny_access
54 rescue_from ::Unauthorized, :with => :deny_access
55 rescue_from ::ActionView::MissingTemplate, :with => :missing_template
55 rescue_from ::ActionView::MissingTemplate, :with => :missing_template
@@ -145,6 +145,18 class ApplicationController < ActionController::Base
145 user
145 user
146 end
146 end
147
147
148 def force_logout_if_password_changed
149 passwd_changed_on = User.current.passwd_changed_on || Time.at(0)
150 # Make sure we force logout only for web browser sessions, not API calls
151 # if the password was changed after the session creation.
152 if session[:user_id] && passwd_changed_on.utc.to_i > session[:ctime].to_i
153 reset_session
154 set_localization
155 flash[:error] = l(:error_session_expired)
156 redirect_to signin_url
157 end
158 end
159
148 def autologin_cookie_name
160 def autologin_cookie_name
149 Redmine::Configuration['autologin_cookie_name'].presence || 'autologin'
161 Redmine::Configuration['autologin_cookie_name'].presence || 'autologin'
150 end
162 end
@@ -100,6 +100,9 class MyController < ApplicationController
100 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
100 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
101 @user.must_change_passwd = false
101 @user.must_change_passwd = false
102 if @user.save
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 flash[:notice] = l(:notice_account_password_updated)
106 flash[:notice] = l(:notice_account_password_updated)
104 redirect_to my_account_path
107 redirect_to my_account_path
105 end
108 end
@@ -279,6 +279,7 class User < Principal
279 def salt_password(clear_password)
279 def salt_password(clear_password)
280 self.salt = User.generate_salt
280 self.salt = User.generate_salt
281 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
281 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
282 self.passwd_changed_on = Time.now
282 end
283 end
283
284
284 # Does the backend storage allow this user to change their password?
285 # Does the backend storage allow this user to change their password?
@@ -185,6 +185,18 class MyControllerTest < ActionController::TestCase
185 assert User.try_to_login('jsmith', 'secret123')
185 assert User.try_to_login('jsmith', 'secret123')
186 end
186 end
187
187
188 def test_change_password_kills_other_sessions
189 @request.session[:ctime] = (Time.now - 30.minutes).utc.to_i
190
191 jsmith = User.find(2)
192 jsmith.passwd_changed_on = 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
188 def test_change_password_should_redirect_if_user_cannot_change_its_password
200 def test_change_password_should_redirect_if_user_cannot_change_its_password
189 User.find(2).update_attribute(:auth_source_id, 1)
201 User.find(2).update_attribute(:auth_source_id, 1)
190
202
General Comments 0
You need to be logged in to leave comments. Login now