FEATURE: Limit the number of active sessions for a user (#8411)

If a user has more than 60 active sessions, the oldest sessions will be terminated automatically. This protects performance when logging in and when loading the list of recently used devices.
This commit is contained in:
David Taylor 2019-11-27 12:39:31 +00:00 committed by GitHub
parent 1a6bbfd10b
commit a227083c1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 0 deletions

View File

@ -8,6 +8,8 @@ class UserAuthToken < ActiveRecord::Base
# used when token did not arrive at client
URGENT_ROTATE_TIME = 1.minute
MAX_SESSION_COUNT = 60
USER_ACTIONS = ['generate']
attr_accessor :unhashed_auth_token
@ -220,6 +222,14 @@ class UserAuthToken < ActiveRecord::Base
end
end
def self.enforce_session_count_limit!(user_id)
tokens_to_destroy = where(user_id: user_id).
where('rotated_at > ?', SiteSetting.maximum_session_age.hours.ago).
order("rotated_at DESC").offset(MAX_SESSION_COUNT)
tokens_to_destroy.delete_all # Returns the number of deleted rows
end
end
# == Schema Information

View File

@ -164,6 +164,9 @@ class Auth::DefaultCurrentUserProvider
unstage_user(user)
make_developer_admin(user)
enable_bootstrap_mode(user)
UserAuthToken.enforce_session_count_limit!(user.id)
@env[CURRENT_USER_KEY] = user
end

View File

@ -586,6 +586,33 @@ describe Auth::DefaultCurrentUserProvider do
expect(UserAuthToken.where(user_id: user.id).count).to eq(2)
end
it "cleans up old sessions when a user logs in" do
user = Fabricate(:user)
yesterday = 1.day.ago
UserAuthToken.insert_all((1..(UserAuthToken::MAX_SESSION_COUNT + 2)).to_a.map do |i|
{
user_id: user.id,
created_at: yesterday + i.seconds,
updated_at: yesterday + i.seconds,
rotated_at: yesterday + i.seconds,
prev_auth_token: "abc#{i}",
auth_token: "abc#{i}"
}
end)
# Check the oldest 3 still exist
expect(UserAuthToken.where(auth_token: (1..3).map { |i| "abc#{i}" }).count).to eq(3)
# On next login, gets fixed
provider('/').log_on_user(user, {}, {})
expect(UserAuthToken.where(user_id: user.id).count).to eq(UserAuthToken::MAX_SESSION_COUNT)
# Oldest sessions are 1, 2, 3. They should now be deleted
expect(UserAuthToken.where(auth_token: (1..3).map { |i| "abc#{i}" }).count).to eq(0)
end
it "sets secure, same site lax cookies" do
SiteSetting.force_https = false
SiteSetting.same_site_cookies = "Lax"