SECURITY: Users can only bookmark posts which they can see.

This commit is contained in:
Guo Xiang Tan 2016-12-21 12:01:26 +08:00
parent c10dfe0d1b
commit 33a05b9406
9 changed files with 91 additions and 33 deletions

View File

@ -7,8 +7,13 @@ class PostActionsController < ApplicationController
def create def create
taken = PostAction.counts_for([@post], current_user)[@post.id] taken = PostAction.counts_for([@post], current_user)[@post.id]
guardian.ensure_post_can_act!(@post, PostActionType.types[@post_action_type_id], taken_actions: taken)
guardian.ensure_post_can_act!(@post, PostActionType.types[@post_action_type_id], is_warning: params[:is_warning]) guardian.ensure_post_can_act!(
@post,
PostActionType.types[@post_action_type_id],
is_warning: params[:is_warning],
taken_actions: taken
)
args = {} args = {}
args[:message] = params[:message] if params[:message].present? args[:message] = params[:message] if params[:message].present?
@ -77,7 +82,6 @@ class PostActionsController < ApplicationController
finder = finder.with_deleted if guardian.is_staff? finder = finder.with_deleted if guardian.is_staff?
@post = finder.first @post = finder.first
guardian.ensure_can_see!(@post)
end end
def fetch_post_action_type_id_from_params def fetch_post_action_type_id_from_params

View File

@ -330,6 +330,8 @@ class TopicsController < ApplicationController
topic = Topic.find(params[:topic_id].to_i) topic = Topic.find(params[:topic_id].to_i)
first_post = topic.ordered_posts.first first_post = topic.ordered_posts.first
guardian.ensure_can_see!(first_post)
PostAction.act(current_user, first_post, PostActionType.types[:bookmark]) PostAction.act(current_user, first_post, PostActionType.types[:bookmark])
render nothing: true render nothing: true

View File

@ -350,8 +350,24 @@ SQL
builder.where('a.action_type <> :pending', pending: UserAction::PENDING) builder.where('a.action_type <> :pending', pending: UserAction::PENDING)
end end
if !guardian.can_see_private_messages?(user_id) || ignore_private_messages if !guardian.can_see_private_messages?(user_id) || ignore_private_messages || !guardian.user
builder.where("t.archetype != :archetype", archetype: Archetype::private_message) builder.where("t.archetype <> :private_message", private_message: Archetype::private_message)
else
unless guardian.is_admin?
sql = <<~SQL
t.archetype <> :private_message OR
EXISTS (
SELECT 1 FROM topic_allowed_users tu WHERE tu.topic_id = t.id AND tu.user_id = :current_user_id
) OR
EXISTS (
SELECT 1 FROM topic_allowed_groups tg WHERE tg.topic_id = t.id AND tg.group_id IN (
SELECT group_id FROM group_users gu WHERE gu.user_id = :current_user_id
)
)
SQL
builder.where(sql, private_message: Archetype::private_message, current_user_id: guardian.user.id)
end
end end
unless guardian.is_admin? unless guardian.is_admin?

View File

@ -4,6 +4,12 @@ module PostGuardian
# Can the user act on the post in a particular way. # Can the user act on the post in a particular way.
# taken_actions = the list of actions the user has already taken # taken_actions = the list of actions the user has already taken
def post_can_act?(post, action_key, opts={}) def post_can_act?(post, action_key, opts={})
return false unless can_see_post?(post)
# no warnings except for staff
return false if (action_key == :notify_user && !is_staff? && opts[:is_warning].present? && opts[:is_warning] == 'true')
taken = opts[:taken_actions].try(:keys).to_a taken = opts[:taken_actions].try(:keys).to_a
is_flag = PostActionType.is_flag?(action_key) is_flag = PostActionType.is_flag?(action_key)
already_taken_this_action = taken.any? && taken.include?(PostActionType.types[action_key]) already_taken_this_action = taken.any? && taken.include?(PostActionType.types[action_key])
@ -32,9 +38,6 @@ module PostGuardian
# new users can't notify_user because they are not allowed to send private messages # new users can't notify_user because they are not allowed to send private messages
not(action_key == :notify_user && !@user.has_trust_level?(SiteSetting.min_trust_to_send_messages)) && not(action_key == :notify_user && !@user.has_trust_level?(SiteSetting.min_trust_to_send_messages)) &&
# non-staff can't send an official warning
not(action_key == :notify_user && !is_staff? && opts[:is_warning].present? && opts[:is_warning] == 'true') &&
# can't send private messages if they're disabled globally # can't send private messages if they're disabled globally
not(action_key == :notify_user && !SiteSetting.enable_private_messages) && not(action_key == :notify_user && !SiteSetting.enable_private_messages) &&
@ -46,7 +49,7 @@ module PostGuardian
end end
def can_defer_flags?(post) def can_defer_flags?(post)
is_staff? && post can_see_post?(post) && is_staff? && post
end end
# Can we see who acted on a post in a particular way? # Can we see who acted on a post in a particular way?
@ -120,6 +123,8 @@ module PostGuardian
# Deleting Methods # Deleting Methods
def can_delete_post?(post) def can_delete_post?(post)
can_see_post?(post)
# Can't delete the first post # Can't delete the first post
return false if post.is_first_post? return false if post.is_first_post?

View File

@ -90,8 +90,9 @@ describe Guardian do
end end
it "returns true for a new user flagging a private message as spam" do it "returns true for a new user flagging a private message as spam" do
post.topic.archetype = Archetype.private_message post = Fabricate(:private_message_post, user: Fabricate(:admin))
user.trust_level = TrustLevel[0] user.trust_level = TrustLevel[0]
post.topic.allowed_users << user
expect(Guardian.new(user).post_can_act?(post, :spam)).to be_truthy expect(Guardian.new(user).post_can_act?(post, :spam)).to be_truthy
end end

View File

@ -7,6 +7,20 @@ describe PostActionsController do
expect { xhr :post, :create }.to raise_error(Discourse::NotLoggedIn) expect { xhr :post, :create }.to raise_error(Discourse::NotLoggedIn)
end end
context 'logged in as user' do
let(:user) { Fabricate(:user) }
let(:private_message) { Fabricate(:private_message_post, user: Fabricate(:coding_horror)) }
before do
log_in_user(user)
end
it 'fails when the user does not have permission to see the post' do
xhr :post, :create, id: private_message.id, post_action_type_id: PostActionType.types[:bookmark]
expect(response).to be_forbidden
end
end
describe 'logged in as moderator' do describe 'logged in as moderator' do
before do before do
@user = log_in(:moderator) @user = log_in(:moderator)
@ -22,13 +36,7 @@ describe PostActionsController do
end end
it "fails when the user doesn't have permission to see the post" do it "fails when the user doesn't have permission to see the post" do
Guardian.any_instance.expects(:can_see?).with(@post).returns(false) @post = Fabricate(:private_message_post, user: Fabricate(:user))
xhr :post, :create, id: @post.id, post_action_type_id: PostActionType.types[:like]
expect(response).to be_forbidden
end
it "fails when the user doesn't have permission to perform that action" do
Guardian.any_instance.expects(:post_can_act?).with(@post, :like, taken_actions: nil).returns(false)
xhr :post, :create, id: @post.id, post_action_type_id: PostActionType.types[:like] xhr :post, :create, id: @post.id, post_action_type_id: PostActionType.types[:like]
expect(response).to be_forbidden expect(response).to be_forbidden
end end

View File

@ -426,13 +426,12 @@ describe PostsController do
include_examples 'action requires login', :put, :bookmark, post_id: 2 include_examples 'action requires login', :put, :bookmark, post_id: 2
describe 'when logged in' do describe 'when logged in' do
let(:post) { Fabricate(:post, user: log_in) } let(:post) { Fabricate(:post, user: log_in) }
let(:private_message) { Fabricate(:private_message_post) }
it "raises an error if the user doesn't have permission to see the post" do it "raises an error if the user doesn't have permission to see the post" do
Guardian.any_instance.expects(:can_see?).with(post).returns(false).once post
xhr :put, :bookmark, post_id: private_message.id, bookmarked: 'true'
xhr :put, :bookmark, post_id: post.id, bookmarked: 'true'
expect(response).to be_forbidden expect(response).to be_forbidden
end end

View File

@ -1224,6 +1224,15 @@ describe TopicsController do
xhr :put, :remove_bookmarks, topic_id: post.topic_id xhr :put, :remove_bookmarks, topic_id: post.topic_id
expect(PostAction.where(user_id: user.id, post_action_type: bookmark).count).to eq(0) expect(PostAction.where(user_id: user.id, post_action_type: bookmark).count).to eq(0)
end end
it "should disallow bookmarks on posts you have no access to" do
log_in
user = Fabricate(:user)
pm = create_post(user: user, archetype: 'private_message', target_usernames: [user.username])
xhr :put, :bookmark, topic_id: pm.topic_id
expect(response).to be_forbidden
end
end end

View File

@ -19,6 +19,7 @@ describe UserAction do
let(:private_topic) do let(:private_topic) do
topic = private_post.topic topic = private_post.topic
topic.update_columns(category_id: nil, archetype: Archetype::private_message) topic.update_columns(category_id: nil, archetype: Archetype::private_message)
TopicAllowedUser.create(topic_id: topic.id, user_id: user.id)
topic topic
end end
@ -223,13 +224,13 @@ describe UserAction do
end end
end end
describe 'private messages' do describe 'secures private messages' do
let(:user) do let(:user) do
Fabricate(:user) Fabricate(:user)
end end
let(:target_user) do let(:user2) do
Fabricate(:user) Fabricate(:user)
end end
@ -237,22 +238,35 @@ describe UserAction do
PostCreator.create( user, PostCreator.create( user,
raw: 'this is a private message', raw: 'this is a private message',
title: 'this is the pm title', title: 'this is the pm title',
target_usernames: target_user.username, target_usernames: user2.username,
archetype: Archetype::private_message archetype: Archetype::private_message
) )
end end
let!(:response) do def count_bookmarks
PostCreator.create(user, raw: 'oops I forgot to mention this', topic_id: private_message.topic_id) UserAction.stream(
user_id: user.id,
action_types: [UserAction::BOOKMARK],
ignore_private_messages: false,
guardian: Guardian.new(user)
).count
end end
let!(:private_message2) do it 'correctly secures stream' do
PostCreator.create( target_user, PostAction.act(user, private_message, PostActionType.types[:bookmark])
raw: 'this is a private message',
title: 'this is the pm title', expect(count_bookmarks).to eq(1)
target_usernames: user.username,
archetype: Archetype::private_message private_message.topic.topic_allowed_users.where(user_id: user.id).destroy_all
)
expect(count_bookmarks).to eq(0)
group = Fabricate(:group)
group.add(user)
private_message.topic.topic_allowed_groups.create(group_id: group.id)
expect(count_bookmarks).to eq(1)
end end
end end