search: fix user-dependent searches showing incorrect paginators.

Some searches, such as searches for private favorites or for the
status:unmoderated tag, return different results for different users.
These searches need to have their counts cached separately for each user
so that we don't return incorrect page counts when two different users
perform the same search.

This can also potentially leak private information, such as the number
of posts flagged, downvoted, or disapproved by a given user.

Partial fix for #4280.
This commit is contained in:
evazion
2020-05-07 20:48:50 -05:00
parent 41c6c882c2
commit 438186a75a
5 changed files with 30 additions and 8 deletions

View File

@@ -4,7 +4,7 @@ class ModqueueController < ApplicationController
def index
authorize :modqueue
@posts = Post.includes(:appeals, :disapprovals, :uploader, flags: [:creator]).pending_or_flagged.available_for_moderation(search_params[:hidden])
@posts = Post.includes(:appeals, :disapprovals, :uploader, flags: [:creator]).pending_or_flagged.available_for_moderation(CurrentUser.user, hidden: search_params[:hidden])
@posts = @posts.paginated_search(params, order: "modqueue", count_pages: true)
@modqueue_posts = @posts.except(:offset, :limit, :order)

View File

@@ -288,7 +288,7 @@ class PostQueryBuilder
when "active"
Post.active
when "unmoderated"
Post.pending_or_flagged.available_for_moderation
Post.pending_or_flagged.available_for_moderation(current_user, hidden: false)
when "all", "any"
Post.all
else
@@ -868,7 +868,19 @@ class PostQueryBuilder
end
def count_cache_key
"pfc:#{to_s}"
if is_user_dependent_search?
"pfc[#{current_user.id.to_i}]:#{to_s}"
else
"pfc:#{to_s}"
end
end
def is_user_dependent_search?
metatags.any? do |metatag|
metatag.name.in?(%w[upvoter upvote downvoter downvote search flagger fav ordfav favgroup ordfavgroup]) ||
metatag.name == "status" && metatag.value == "unmoderated" ||
metatag.name == "disapproved" && User.normalize_name(metatag.value) == current_user.name
end
end
end

View File

@@ -1453,7 +1453,7 @@ class Post < ApplicationRecord
from(relation.arel.as("posts"))
end
def available_for_moderation(hidden = false, user = CurrentUser.user)
def available_for_moderation(user, hidden: false)
return none if user.is_anonymous?
approved_posts = user.post_approvals.select(:post_id)

View File

@@ -35,8 +35,8 @@ class PostDisapprovalTest < ActiveSupport::TestCase
end
should "remove the associated post from alice's moderation queue" do
assert(!Post.available_for_moderation(false).map(&:id).include?(@post_1.id))
assert(Post.available_for_moderation(false).map(&:id).include?(@post_2.id))
assert(!Post.available_for_moderation(CurrentUser.user, hidden: false).map(&:id).include?(@post_1.id))
assert(Post.available_for_moderation(CurrentUser.user, hidden: false).map(&:id).include?(@post_2.id))
end
end
@@ -47,8 +47,8 @@ class PostDisapprovalTest < ActiveSupport::TestCase
end
should "not remove the associated post from brittony's moderation queue" do
assert(Post.available_for_moderation(false).map(&:id).include?(@post_1.id))
assert(Post.available_for_moderation(false).map(&:id).include?(@post_2.id))
assert(Post.available_for_moderation(CurrentUser.user, hidden: false).map(&:id).include?(@post_1.id))
assert(Post.available_for_moderation(CurrentUser.user, hidden: false).map(&:id).include?(@post_2.id))
end
end
end

View File

@@ -1153,5 +1153,15 @@ class PostQueryBuilderTest < ActiveSupport::TestCase
end
end
end
context "for a user-dependent metatag" do
should "cache the count separately for different users" do
@user = create(:user, enable_private_favorites: true)
@post = as(@user) { create(:post, tag_string: "fav:#{@user.name}") }
assert_equal(1, PostQueryBuilder.new("fav:#{@user.name}", @user).fast_count)
assert_equal(0, PostQueryBuilder.new("fav:#{@user.name}").fast_count)
end
end
end
end