Invalidate existing sessions when changing email or password

As we don't have any way to actually find the active sessions for
an account we instead store a fingerprint in the session, and refuse
to use any session with a different fingerprint.
This commit is contained in:
Tom Hughes 2020-09-29 14:22:51 +01:00
parent c694c78c9a
commit 7db541d697
3 changed files with 21 additions and 1 deletions

View file

@ -23,7 +23,11 @@ class ApplicationController < ActionController::Base
if session[:user] if session[:user]
self.current_user = User.where(:id => session[:user]).where("status IN ('active', 'confirmed', 'suspended')").first self.current_user = User.where(:id => session[:user]).where("status IN ('active', 'confirmed', 'suspended')").first
if current_user.status == "suspended" if session[:fingerprint] &&
session[:fingerprint] != current_user.fingerprint
reset_session
self.current_user = nil
elsif current_user.status == "suspended"
session.delete(:user) session.delete(:user)
session_expires_automatically session_expires_automatically
@ -42,6 +46,8 @@ class ApplicationController < ActionController::Base
elsif session[:token] elsif session[:token]
session[:user] = current_user.id if self.current_user = User.authenticate(:token => session[:token]) session[:user] = current_user.id if self.current_user = User.authenticate(:token => session[:token])
end end
session[:fingerprint] = current_user.fingerprint if current_user && session[:fingerprint].nil?
rescue StandardError => e rescue StandardError => e
logger.info("Exception authorizing user: #{e}") logger.info("Exception authorizing user: #{e}")
reset_session reset_session

View file

@ -183,6 +183,7 @@ class UsersController < ApplicationController
if current_user.save if current_user.save
token.destroy token.destroy
session[:fingerprint] = current_user.fingerprint
flash[:notice] = t "users.reset_password.flash changed" flash[:notice] = t "users.reset_password.flash changed"
successful_login(current_user) successful_login(current_user)
end end
@ -323,6 +324,7 @@ class UsersController < ApplicationController
token.destroy token.destroy
session[:user] = user.id session[:user] = user.id
session[:fingerprint] = user.fingerprint
redirect_to referer || welcome_path redirect_to referer || welcome_path
end end
@ -368,6 +370,7 @@ class UsersController < ApplicationController
end end
current_user.tokens.delete_all current_user.tokens.delete_all
session[:user] = current_user.id session[:user] = current_user.id
session[:fingerprint] = current_user.fingerprint
redirect_to :action => "account", :display_name => current_user.display_name redirect_to :action => "account", :display_name => current_user.display_name
elsif token elsif token
flash[:error] = t "users.confirm_email.failure" flash[:error] = t "users.confirm_email.failure"
@ -552,6 +555,7 @@ class UsersController < ApplicationController
# process a successful login # process a successful login
def successful_login(user, referer = nil) def successful_login(user, referer = nil)
session[:user] = user.id session[:user] = user.id
session[:fingerprint] = user.fingerprint
session_expires_after 28.days if session[:remember_me] session_expires_after 28.days if session[:remember_me]
target = referer || session[:referer] || url_for(:controller => :site, :action => :index) target = referer || session[:referer] || url_for(:controller => :site, :action => :index)
@ -642,6 +646,8 @@ class UsersController < ApplicationController
end end
if user.save if user.save
session[:fingerprint] = user.fingerprint
set_locale(true) set_locale(true)
if user.new_email.blank? || user.new_email == user.email if user.new_email.blank? || user.new_email == user.email

View file

@ -44,6 +44,7 @@
# #
class User < ApplicationRecord class User < ApplicationRecord
require "digest"
require "xml/libxml" require "xml/libxml"
has_many :traces, -> { where(:visible => true) } has_many :traces, -> { where(:visible => true) }
@ -306,6 +307,13 @@ class User < ApplicationRecord
ClientApplication.find_by(:key => application_key).access_token_for_user(self) ClientApplication.find_by(:key => application_key).access_token_for_user(self)
end end
def fingerprint
digest = Digest::SHA256.new
digest.update(email)
digest.update(pass_crypt)
digest.hexdigest
end
private private
def set_defaults def set_defaults