mirror of
https://github.com/discourse/discourse.git
synced 2025-05-24 14:12:10 +08:00
FIX: avoid concurrent usage of AR models (#16596)
Flagged by the truffle team at: https://meta.discourse.org/t/thread-unsafe-current-user-usage-in-auth-defaultcurrentuserprovider/225671 This usage of AR is unsafe currently, as AR models are not safe for concurrent usage Introduces a new query potentially every minute which should be acceptable.
This commit is contained in:
@ -752,12 +752,17 @@ class User < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_ip_address!(new_ip_address)
|
def self.update_ip_address!(user_id, new_ip:, old_ip:)
|
||||||
unless ip_address == new_ip_address || new_ip_address.blank?
|
unless old_ip == new_ip || new_ip.blank?
|
||||||
update_column(:ip_address, new_ip_address)
|
|
||||||
|
DB.exec(<<~SQL, user_id: user_id, ip_address: new_ip)
|
||||||
|
UPDATE users
|
||||||
|
SET ip_address = :ip_address
|
||||||
|
WHERE id = :user_id
|
||||||
|
SQL
|
||||||
|
|
||||||
if SiteSetting.keep_old_ip_address_count > 0
|
if SiteSetting.keep_old_ip_address_count > 0
|
||||||
DB.exec(<<~SQL, user_id: self.id, ip_address: new_ip_address, current_timestamp: Time.zone.now)
|
DB.exec(<<~SQL, user_id: user_id, ip_address: new_ip, current_timestamp: Time.zone.now)
|
||||||
INSERT INTO user_ip_address_histories (user_id, ip_address, created_at, updated_at)
|
INSERT INTO user_ip_address_histories (user_id, ip_address, created_at, updated_at)
|
||||||
VALUES (:user_id, :ip_address, :current_timestamp, :current_timestamp)
|
VALUES (:user_id, :ip_address, :current_timestamp, :current_timestamp)
|
||||||
ON CONFLICT (user_id, ip_address)
|
ON CONFLICT (user_id, ip_address)
|
||||||
@ -765,7 +770,7 @@ class User < ActiveRecord::Base
|
|||||||
UPDATE SET updated_at = :current_timestamp
|
UPDATE SET updated_at = :current_timestamp
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
DB.exec(<<~SQL, user_id: self.id, offset: SiteSetting.keep_old_ip_address_count)
|
DB.exec(<<~SQL, user_id: user_id, offset: SiteSetting.keep_old_ip_address_count)
|
||||||
DELETE FROM user_ip_address_histories
|
DELETE FROM user_ip_address_histories
|
||||||
WHERE id IN (
|
WHERE id IN (
|
||||||
SELECT
|
SELECT
|
||||||
@ -780,24 +785,36 @@ class User < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def last_seen_redis_key(now)
|
def update_ip_address!(new_ip_address)
|
||||||
|
User.update_ip_address!(id, new_ip: new_ip_address, old_ip: ip_address)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.last_seen_redis_key(user_id, now)
|
||||||
now_date = now.to_date
|
now_date = now.to_date
|
||||||
"user:#{id}:#{now_date}"
|
"user:#{user_id}:#{now_date}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_seen_redis_key(now)
|
||||||
|
User.last_seen_redis_key(id, now)
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_last_seen_cache!(now = Time.zone.now)
|
def clear_last_seen_cache!(now = Time.zone.now)
|
||||||
Discourse.redis.del(last_seen_redis_key(now))
|
Discourse.redis.del(last_seen_redis_key(now))
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_last_seen!(now = Time.zone.now)
|
def self.should_update_last_seen?(user_id, now = Time.zone.now)
|
||||||
redis_key = last_seen_redis_key(now)
|
return true if SiteSetting.active_user_rate_limit_secs <= 0
|
||||||
|
|
||||||
if SiteSetting.active_user_rate_limit_secs > 0
|
Discourse.redis.set(
|
||||||
return if !Discourse.redis.set(
|
last_seen_redis_key(user_id, now), "1",
|
||||||
redis_key, "1",
|
nx: true,
|
||||||
nx: true,
|
ex: SiteSetting.active_user_rate_limit_secs
|
||||||
ex: SiteSetting.active_user_rate_limit_secs
|
)
|
||||||
)
|
end
|
||||||
|
|
||||||
|
def update_last_seen!(now = Time.zone.now, force: false)
|
||||||
|
if !force
|
||||||
|
return if !User.should_update_last_seen?(self.id, now)
|
||||||
end
|
end
|
||||||
|
|
||||||
update_previous_visit(now)
|
update_previous_visit(now)
|
||||||
|
@ -217,12 +217,17 @@ class Auth::DefaultCurrentUserProvider
|
|||||||
end
|
end
|
||||||
|
|
||||||
if current_user && should_update_last_seen?
|
if current_user && should_update_last_seen?
|
||||||
u = current_user
|
|
||||||
ip = request.ip
|
ip = request.ip
|
||||||
|
user_id = current_user.id
|
||||||
|
old_ip = current_user.ip_address
|
||||||
|
|
||||||
Scheduler::Defer.later "Updating Last Seen" do
|
Scheduler::Defer.later "Updating Last Seen" do
|
||||||
u.update_last_seen!
|
if User.should_update_last_seen?(user_id)
|
||||||
u.update_ip_address!(ip)
|
if u = User.find_by(id: user_id)
|
||||||
|
u.update_last_seen!(Time.zone.now, force: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
User.update_ip_address!(user_id, new_ip: ip, old_ip: old_ip)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user