mirror of
https://github.com/discourse/discourse.git
synced 2024-12-19 07:15:58 +08:00
FEATURE: Restrict profile visibility of low-trust users (#29981)
We've seen in some communities abuse of user profile where bios and other fields are used in malicious ways, such as malware distribution. A common pattern between all the abuse cases we've seen is that the malicious actors tend to have 0 posts and have a low trust level. To eliminate this abuse vector, or at least make it much less effective, we're making the following changes to user profiles: 1. Anonymous, TL0 and TL1 users cannot see any user profiles for users with 0 posts except for staff users 2. Anonymous and TL0 users can only see profiles of TL1 users and above Users can always see their own profile, and they can still hide their profiles via the "Hide my public profile" preference. Staff can always see any user's profile. Internal topic: t/142853.
This commit is contained in:
parent
5e86bc2f43
commit
976aca68f6
|
@ -128,12 +128,21 @@ module UserGuardian
|
||||||
|
|
||||||
def can_see_profile?(user)
|
def can_see_profile?(user)
|
||||||
return false if user.blank?
|
return false if user.blank?
|
||||||
return true if !SiteSetting.allow_users_to_hide_profile?
|
return true if is_me?(user) || is_staff?
|
||||||
|
|
||||||
# If a user has hidden their profile, restrict it to them and staff
|
profile_hidden = SiteSetting.allow_users_to_hide_profile && user.user_option&.hide_profile?
|
||||||
return is_me?(user) || is_staff? if user.user_option.try(:hide_profile?)
|
|
||||||
|
|
||||||
true
|
return true if user.staff? && !profile_hidden
|
||||||
|
|
||||||
|
if user.user_stat.blank? || user.user_stat.post_count == 0
|
||||||
|
return false if anonymous? || !@user.has_trust_level?(TrustLevel[2])
|
||||||
|
end
|
||||||
|
|
||||||
|
if anonymous? || !@user.has_trust_level?(TrustLevel[1])
|
||||||
|
return user.has_trust_level?(TrustLevel[1]) && !profile_hidden
|
||||||
|
end
|
||||||
|
|
||||||
|
!profile_hidden
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_see_user_actions?(user, action_types)
|
def can_see_user_actions?(user, action_types)
|
||||||
|
|
|
@ -4,7 +4,10 @@ RSpec.describe "User profile", type: :system do
|
||||||
fab!(:current_user) { Fabricate(:user) }
|
fab!(:current_user) { Fabricate(:user) }
|
||||||
fab!(:user)
|
fab!(:user)
|
||||||
|
|
||||||
before { chat_system_bootstrap }
|
before do
|
||||||
|
user.user_stat.update!(post_count: 1)
|
||||||
|
chat_system_bootstrap
|
||||||
|
end
|
||||||
|
|
||||||
shared_examples "not showing chat button" do
|
shared_examples "not showing chat button" do
|
||||||
it "has no chat button" do
|
it "has no chat button" do
|
||||||
|
|
|
@ -98,44 +98,175 @@ RSpec.describe UserGuardian do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#can_see_profile?" do
|
describe "#can_see_profile?" do
|
||||||
|
fab!(:tl0_user) { Fabricate(:user, trust_level: 0) }
|
||||||
|
fab!(:tl1_user) { Fabricate(:user, trust_level: 1) }
|
||||||
|
fab!(:tl2_user) { Fabricate(:user, trust_level: 2) }
|
||||||
|
|
||||||
|
before { tl2_user.user_stat.update!(post_count: 1) }
|
||||||
|
|
||||||
|
context "when viewing the profile of a user with 0 posts" do
|
||||||
|
before { user.user_stat.update!(post_count: 0) }
|
||||||
|
|
||||||
|
it "they can view their own profile" do
|
||||||
|
expect(Guardian.new(user).can_see_profile?(user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "an anonymous user cannot view the user's profile" do
|
||||||
|
expect(Guardian.new.can_see_profile?(user)).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a TL0 user cannot view the user's profile" do
|
||||||
|
expect(Guardian.new(tl0_user).can_see_profile?(user)).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a TL1 user cannot view the user's profile" do
|
||||||
|
expect(Guardian.new(tl1_user).can_see_profile?(user)).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a TL2 user can view the user's profile" do
|
||||||
|
expect(Guardian.new(tl2_user).can_see_profile?(user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a moderator can view the user's profile" do
|
||||||
|
expect(Guardian.new(moderator).can_see_profile?(user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "an admin can view the user's profile" do
|
||||||
|
expect(Guardian.new(admin).can_see_profile?(user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the profile is hidden" do
|
||||||
|
before do
|
||||||
|
SiteSetting.allow_users_to_hide_profile = true
|
||||||
|
user.user_option.update!(hide_profile: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "they can view their own profile" do
|
||||||
|
expect(Guardian.new(user).can_see_profile?(user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a TL2 user cannot view the user's profile" do
|
||||||
|
expect(Guardian.new(tl2_user).can_see_profile?(user)).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a moderator can view the user's profile" do
|
||||||
|
expect(Guardian.new(moderator).can_see_profile?(user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "an admin can view the user's profile" do
|
||||||
|
expect(Guardian.new(admin).can_see_profile?(user)).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when viewing the profile of a TL0 user with more than 0 posts" do
|
||||||
|
before { tl0_user.user_stat.update!(post_count: 1) }
|
||||||
|
|
||||||
|
it "they can view their own profile" do
|
||||||
|
expect(Guardian.new(tl0_user).can_see_profile?(tl0_user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "an anonymous user cannot view the user's profile" do
|
||||||
|
expect(Guardian.new.can_see_profile?(tl0_user)).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a TL0 user cannot view the user's profile" do
|
||||||
|
expect(Guardian.new(Fabricate(:user, trust_level: 0)).can_see_profile?(tl0_user)).to eq(
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a TL1 user can view the user's profile" do
|
||||||
|
expect(Guardian.new(tl1_user).can_see_profile?(tl0_user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a TL2 user can view the user's profile" do
|
||||||
|
expect(Guardian.new(tl2_user).can_see_profile?(tl0_user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a moderator user can view the user's profile" do
|
||||||
|
expect(Guardian.new(moderator).can_see_profile?(tl0_user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "an admin user can view the user's profile" do
|
||||||
|
expect(Guardian.new(admin).can_see_profile?(tl0_user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the profile is hidden" do
|
||||||
|
before do
|
||||||
|
SiteSetting.allow_users_to_hide_profile = true
|
||||||
|
tl0_user.user_option.update!(hide_profile: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "they can view their own profile" do
|
||||||
|
expect(Guardian.new(tl0_user).can_see_profile?(tl0_user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a TL1 user cannot view the user's profile" do
|
||||||
|
expect(Guardian.new(tl1_user).can_see_profile?(tl0_user)).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a TL2 user cannot view the user's profile" do
|
||||||
|
expect(Guardian.new(tl2_user).can_see_profile?(tl0_user)).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "a moderator user can view the user's profile" do
|
||||||
|
expect(Guardian.new(moderator).can_see_profile?(tl0_user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "an admin user can view the user's profile" do
|
||||||
|
expect(Guardian.new(admin).can_see_profile?(tl0_user)).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the allow_users_to_hide_profile setting is false" do
|
||||||
|
before { SiteSetting.allow_users_to_hide_profile = false }
|
||||||
|
|
||||||
|
it "doesn't hide the profile even if the hide_profile user option is true" do
|
||||||
|
tl2_user.user_option.update!(hide_profile: true)
|
||||||
|
|
||||||
|
expect(Guardian.new(tl0_user).can_see_profile?(tl2_user)).to eq(true)
|
||||||
|
expect(Guardian.new(tl1_user).can_see_profile?(tl2_user)).to eq(true)
|
||||||
|
expect(Guardian.new(admin).can_see_profile?(tl2_user)).to eq(true)
|
||||||
|
expect(Guardian.new(moderator).can_see_profile?(tl2_user)).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the allow_users_to_hide_profile setting is true" do
|
||||||
|
before { SiteSetting.allow_users_to_hide_profile = true }
|
||||||
|
|
||||||
|
it "doesn't allow non-staff users to view the user's profile if the hide_profile user option is true" do
|
||||||
|
tl2_user.user_option.update!(hide_profile: true)
|
||||||
|
|
||||||
|
expect(Guardian.new(tl0_user).can_see_profile?(tl2_user)).to eq(false)
|
||||||
|
expect(Guardian.new(tl1_user).can_see_profile?(tl2_user)).to eq(false)
|
||||||
|
|
||||||
|
expect(Guardian.new(admin).can_see_profile?(tl2_user)).to eq(true)
|
||||||
|
expect(Guardian.new(moderator).can_see_profile?(tl2_user)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows everyone to view the user's profile if the hide_profile user option is false" do
|
||||||
|
tl2_user.user_option.update!(hide_profile: false)
|
||||||
|
|
||||||
|
expect(Guardian.new(tl0_user).can_see_profile?(tl2_user)).to eq(true)
|
||||||
|
expect(Guardian.new(tl1_user).can_see_profile?(tl2_user)).to eq(true)
|
||||||
|
|
||||||
|
expect(Guardian.new(admin).can_see_profile?(tl2_user)).to eq(true)
|
||||||
|
expect(Guardian.new(moderator).can_see_profile?(tl2_user)).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "is false for no user" do
|
it "is false for no user" do
|
||||||
expect(Guardian.new.can_see_profile?(nil)).to eq(false)
|
expect(Guardian.new.can_see_profile?(nil)).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is true for a user whose profile is public" do
|
it "is true for staff users even when they have no posts" do
|
||||||
expect(Guardian.new.can_see_profile?(user)).to eq(true)
|
admin.user_stat.update!(post_count: 0)
|
||||||
end
|
moderator.user_stat.update!(post_count: 0)
|
||||||
|
|
||||||
context "with hidden profile" do
|
expect(Guardian.new.can_see_profile?(admin)).to eq(true)
|
||||||
# Mixing Fabricate.build() and Fabricate() could cause ID clashes, so override :user
|
expect(Guardian.new.can_see_profile?(moderator)).to eq(true)
|
||||||
fab!(:user)
|
|
||||||
|
|
||||||
let(:hidden_user) do
|
|
||||||
result = Fabricate(:user)
|
|
||||||
result.user_option.update_column(:hide_profile, true)
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is false for another user" do
|
|
||||||
expect(Guardian.new(user).can_see_profile?(hidden_user)).to eq(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is false for an anonymous user" do
|
|
||||||
expect(Guardian.new.can_see_profile?(hidden_user)).to eq(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is true for the user themselves" do
|
|
||||||
expect(Guardian.new(hidden_user).can_see_profile?(hidden_user)).to eq(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is true for a staff user" do
|
|
||||||
expect(Guardian.new(admin).can_see_profile?(hidden_user)).to eq(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is true if hiding profiles is disabled" do
|
|
||||||
SiteSetting.allow_users_to_hide_profile = false
|
|
||||||
expect(Guardian.new(user).can_see_profile?(hidden_user)).to eq(true)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -905,7 +905,10 @@ RSpec.describe ListController do
|
||||||
fab!(:topic2) { Fabricate(:topic, user: user) }
|
fab!(:topic2) { Fabricate(:topic, user: user) }
|
||||||
fab!(:user2) { Fabricate(:user) }
|
fab!(:user2) { Fabricate(:user) }
|
||||||
|
|
||||||
before { sign_in(user2) }
|
before do
|
||||||
|
user.user_stat.update!(post_count: 1)
|
||||||
|
sign_in(user2)
|
||||||
|
end
|
||||||
|
|
||||||
it "should respond with a list" do
|
it "should respond with a list" do
|
||||||
get "/topics/created-by/#{user.username}.json"
|
get "/topics/created-by/#{user.username}.json"
|
||||||
|
|
|
@ -2578,6 +2578,8 @@ RSpec.describe PostsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#user_posts_feed" do
|
describe "#user_posts_feed" do
|
||||||
|
before { user.user_stat.update!(post_count: 1) }
|
||||||
|
|
||||||
it "returns public posts rss feed" do
|
it "returns public posts rss feed" do
|
||||||
public_post
|
public_post
|
||||||
private_post
|
private_post
|
||||||
|
|
|
@ -19,7 +19,10 @@ RSpec.describe UserActionsController do
|
||||||
let(:actions) { response.parsed_body["user_actions"] }
|
let(:actions) { response.parsed_body["user_actions"] }
|
||||||
let(:post) { create_post }
|
let(:post) { create_post }
|
||||||
|
|
||||||
before { UserActionManager.enable }
|
before do
|
||||||
|
UserActionManager.enable
|
||||||
|
post.user.user_stat.update!(post_count: 1)
|
||||||
|
end
|
||||||
|
|
||||||
it "renders list correctly" do
|
it "renders list correctly" do
|
||||||
user_actions
|
user_actions
|
||||||
|
|
|
@ -5,6 +5,8 @@ RSpec.describe UserBadgesController do
|
||||||
fab!(:admin)
|
fab!(:admin)
|
||||||
fab!(:badge)
|
fab!(:badge)
|
||||||
|
|
||||||
|
before { user.user_stat.update!(post_count: 1) }
|
||||||
|
|
||||||
describe "#index" do
|
describe "#index" do
|
||||||
fab!(:badge) { Fabricate(:badge, target_posts: true, show_posts: false) }
|
fab!(:badge) { Fabricate(:badge, target_posts: true, show_posts: false) }
|
||||||
|
|
||||||
|
|
|
@ -4252,6 +4252,12 @@ RSpec.describe UsersController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#summary" do
|
describe "#summary" do
|
||||||
|
before do
|
||||||
|
user.user_stat.update!(post_count: 1)
|
||||||
|
user1.user_stat.update!(post_count: 1)
|
||||||
|
user_deferred.user_stat.update!(post_count: 1)
|
||||||
|
end
|
||||||
|
|
||||||
it "generates summary info" do
|
it "generates summary info" do
|
||||||
create_post(user: user)
|
create_post(user: user)
|
||||||
|
|
||||||
|
@ -4261,7 +4267,7 @@ RSpec.describe UsersController do
|
||||||
json = response.parsed_body
|
json = response.parsed_body
|
||||||
|
|
||||||
expect(json["user_summary"]["topic_count"]).to eq(1)
|
expect(json["user_summary"]["topic_count"]).to eq(1)
|
||||||
expect(json["user_summary"]["post_count"]).to eq(0)
|
expect(json["user_summary"]["post_count"]).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when `hide_user_profiles_from_public` site setting is enabled" do
|
context "when `hide_user_profiles_from_public` site setting is enabled" do
|
||||||
|
@ -4938,6 +4944,8 @@ RSpec.describe UsersController do
|
||||||
fab!(:user) { Discourse.system_user }
|
fab!(:user) { Discourse.system_user }
|
||||||
fab!(:user2) { Fabricate(:user) }
|
fab!(:user2) { Fabricate(:user) }
|
||||||
|
|
||||||
|
before { user2.user_stat.update!(post_count: 1) }
|
||||||
|
|
||||||
it "returns success" do
|
it "returns success" do
|
||||||
get "/user-cards.json?user_ids=#{user.id},#{user2.id}"
|
get "/user-cards.json?user_ids=#{user.id},#{user2.id}"
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
RSpec.describe UserSerializer do
|
RSpec.describe UserSerializer do
|
||||||
fab!(:user) { Fabricate(:user, trust_level: 0) }
|
fab!(:user) { Fabricate(:user, trust_level: 0) }
|
||||||
|
|
||||||
|
before { user.user_stat.update!(post_count: 1) }
|
||||||
|
|
||||||
context "with a TL0 user seen as anonymous" do
|
context "with a TL0 user seen as anonymous" do
|
||||||
let(:serializer) { UserSerializer.new(user, scope: Guardian.new, root: false) }
|
let(:serializer) { UserSerializer.new(user, scope: Guardian.new, root: false) }
|
||||||
let(:json) { serializer.as_json }
|
let(:json) { serializer.as_json }
|
||||||
|
|
Loading…
Reference in New Issue
Block a user