FIX: Make deleted topic post bookmarks more resilient (#10619)

This PR ensures that new bookmarks cannot be created for deleted posts and topics, and also makes sure that if a bookmark was created and then the topic deleted that the show topic page does not error from trying to retrieve the bookmark reminder at.
This commit is contained in:
Martin Brennan 2020-09-07 14:52:14 +10:00 committed by GitHub
parent f2842490d3
commit 431bd84dec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 3 deletions

View File

@ -8,10 +8,15 @@ class BookmarkManager
end end
def create(post_id:, name: nil, reminder_type: nil, reminder_at: nil, options: {}) def create(post_id:, name: nil, reminder_type: nil, reminder_at: nil, options: {})
post = Post.unscoped.includes(:topic).find(post_id) post = Post.find_by(id: post_id)
reminder_type = parse_reminder_type(reminder_type) reminder_type = parse_reminder_type(reminder_type)
raise Discourse::InvalidAccess.new if !Guardian.new(@user).can_see_post?(post) # no bookmarking deleted posts or topics
raise Discourse::InvalidAccess if post.blank? || post.topic.blank?
if !Guardian.new(@user).can_see_post?(post) || !Guardian.new(@user).can_see_topic?(post.topic)
raise Discourse::InvalidAccess
end
bookmark = Bookmark.create( bookmark = Bookmark.create(
{ {

View File

@ -356,7 +356,8 @@ class TopicView
end end
def first_post_bookmark_reminder_at def first_post_bookmark_reminder_at
@topic.first_post.bookmarks.where(user: @user).pluck_first(:reminder_at) @topic.posts.with_deleted.where(post_number: 1).first
.bookmarks.where(user: @user).pluck_first(:reminder_at)
end end
MAX_PARTICIPANTS = 24 MAX_PARTICIPANTS = 24

View File

@ -349,6 +349,24 @@ describe TopicView do
end end
end end
context "#first_post_bookmark_reminder_at" do
let!(:user) { Fabricate(:user) }
let!(:bookmark1) { Fabricate(:bookmark_next_business_day_reminder, post: topic.first_post, user: user) }
it "gets the first post bookmark reminder at for the user" do
expect(TopicView.new(topic.id, user).first_post_bookmark_reminder_at).to eq_time(bookmark1.reminder_at)
end
context "when the topic is deleted" do
it "gets the first post bookmark reminder at for the user" do
topic_view = TopicView.new(topic, user)
PostDestroyer.new(Fabricate(:admin), topic.first_post).destroy
topic.reload
expect(topic_view.first_post_bookmark_reminder_at).to eq_time(bookmark1.reminder_at)
end
end
end
context '.topic_user' do context '.topic_user' do
it 'returns nil when there is no user' do it 'returns nil when there is no user' do
expect(TopicView.new(topic.id, nil).topic_user).to be_blank expect(TopicView.new(topic.id, nil).topic_user).to be_blank

View File

@ -21,6 +21,16 @@ RSpec.describe BookmarkManager do
expect(bookmark.topic_id).to eq(post.topic_id) expect(bookmark.topic_id).to eq(post.topic_id)
end end
it "when topic is deleted it raises invalid access from guardian check" do
post.topic.trash!
expect { subject.create(post_id: post.id, name: name) }.to raise_error(Discourse::InvalidAccess)
end
it "when post is deleted it raises invalid access from guardian check" do
post.trash!
expect { subject.create(post_id: post.id, name: name) }.to raise_error(Discourse::InvalidAccess)
end
it "updates the topic user bookmarked column to true if any post is bookmarked" do it "updates the topic user bookmarked column to true if any post is bookmarked" do
subject.create(post_id: post.id, name: name, reminder_type: reminder_type, reminder_at: reminder_at) subject.create(post_id: post.id, name: name, reminder_type: reminder_type, reminder_at: reminder_at)
tu = TopicUser.find_by(user: user) tu = TopicUser.find_by(user: user)