FIX: properly ban non human users from draft system

Previously we had a partial fix in place where non human users
were not allowed draft sequences, this left edges around where non
human users asked for drafts yet had none.

For example system could already have a few drafts in place.

This also removes and extensibility point we added that is not in use
This commit is contained in:
Sam Saffron 2020-05-26 10:07:00 +10:00
parent 979093787f
commit fc97f7e0e7
No known key found for this signature in database
GPG Key ID: B9606168D2FFD9F5
5 changed files with 31 additions and 23 deletions

View File

@ -10,6 +10,8 @@ class Draft < ActiveRecord::Base
class OutOfSequence < StandardError; end
def self.set(user, key, sequence, data, owner = nil)
return 0 if !User.human_user_id?(user.id)
if SiteSetting.backup_drafts_to_pm_length > 0 && SiteSetting.backup_drafts_to_pm_length < data.length
backup_draft(user, key, sequence, data)
end
@ -94,6 +96,7 @@ class Draft < ActiveRecord::Base
end
def self.get(user, key, sequence)
return if !user || !user.id || !User.human_user_id?(user.id)
opts = {
user_id: user.id,
@ -128,6 +131,8 @@ class Draft < ActiveRecord::Base
end
def self.clear(user, key, sequence)
return if !user || !user.id || !User.human_user_id?(user.id)
current_sequence = DraftSequence.current(user, key)
# bad caller is a reason to complain

View File

@ -2,10 +2,12 @@
class DraftSequence < ActiveRecord::Base
def self.next!(user, key)
return nil if !user
user_id = user
user_id = user.id unless user.is_a?(Integer)
return 0 if invalid_user_id?(user_id)
return 0 if !User.human_user_id?(user_id)
h = { user_id: user_id, draft_key: key }
c = DraftSequence.find_by(h)
@ -18,27 +20,17 @@ class DraftSequence < ActiveRecord::Base
end
def self.current(user, key)
return nil unless user
return nil if !user
user_id = user
user_id = user.id unless user.is_a?(Integer)
return 0 if invalid_user_id?(user_id)
return 0 if !User.human_user_id?(user_id)
# perf critical path
r, _ = DB.query_single('select sequence from draft_sequences where user_id = ? and draft_key = ?', user_id, key)
r.to_i
end
cattr_accessor :plugin_ignore_draft_sequence_callbacks
self.plugin_ignore_draft_sequence_callbacks = {}
def self.invalid_user_id?(user_id)
user_id < 0 || self.plugin_ignore_draft_sequence_callbacks.any? do |plugin, callback|
plugin.enabled? ? callback.call(user_id) : false
end
end
private_class_method :invalid_user_id?
end
# == Schema Information

View File

@ -306,8 +306,12 @@ class User < ActiveRecord::Base
fields.uniq
end
def self.human_user_id?(user_id)
user_id > 0
end
def human?
self.id > 0
User.human_user_id?(self.id)
end
def bot?

View File

@ -405,15 +405,6 @@ class Plugin::Instance
SeedFu.fixture_paths.concat(paths)
end
# Applies to all sites in a multisite environment. Block is not called if
# plugin is not enabled. Block is called with `user_id` and has to return a
# boolean based on whether the given `user_id` should be ignored.
def register_ignore_draft_sequence_callback(&block)
reloadable_patch do |plugin|
::DraftSequence.plugin_ignore_draft_sequence_callbacks[plugin] = block
end
end
def listen_for(event_name)
return unless self.respond_to?(event_name)
DiscourseEvent.on(event_name, &self.method(event_name))

View File

@ -12,6 +12,22 @@ describe Draft do
Fabricate(:post)
end
context 'system user' do
it "can not set drafts" do
# fake a sequence
DraftSequence.create!(user_id: Discourse.system_user.id, draft_key: "abc", sequence: 10)
seq = Draft.set(Discourse.system_user, "abc", 0, { reply: 'hi' }.to_json)
expect(seq).to eq(0)
draft = Draft.get(Discourse.system_user, "abc", 0)
expect(draft).to eq(nil)
draft = Draft.get(Discourse.system_user, "abc", 1)
expect(draft).to eq(nil)
end
end
context 'backup_drafts_to_pm_length' do
it "correctly backs up drafts to a personal message" do
SiteSetting.backup_drafts_to_pm_length = 1