From c752a46c457759149c14ae0e4d501d5ef2ce478a Mon Sep 17 00:00:00 2001 From: Fire Demon Date: Tue, 21 Jul 2020 23:40:01 -0500 Subject: [Privacy] Implement thread ownership and visibility --- app/lib/activitypub/activity/create.rb | 3 +- app/lib/feed_manager.rb | 4 +++ app/models/concerns/account_associations.rb | 3 ++ app/models/conversation.rb | 3 ++ app/models/status.rb | 23 ++++++------- app/policies/status_policy.rb | 52 +++++++++++++++++++++++++---- 6 files changed, 68 insertions(+), 20 deletions(-) (limited to 'app') diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index d2bbd26d5..fbb4624e3 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -383,7 +383,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity return Conversation.find_by(id: OStatus::TagManager.instance.unique_tag_to_local_id(uri, 'Conversation')) if OStatus::TagManager.instance.local_id?(uri) begin - Conversation.find_or_create_by!(uri: uri) + conversation = Conversation.find_by(uri: uri) + Conversation.create!(uri: uri, account: @account, public: %i(public unlisted).include?(visibility_from_audience)) if conversation.nil? rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique retry end diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 92c125c0f..49db68119 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -56,6 +56,7 @@ class FeedManager should_filter &&= status.account_id == list.account_id should_filter &&= !list.show_all_replies? should_filter &&= !(list.show_list_replies? && ListAccount.where(list_id: list.id, account_id: status.in_reply_to_account_id).exists?) + should_filter &&= !(list.show_list_replies? && status.conversation&.account_id.present? && ListAccount.where(list_id: list.id, account_id: status.conversation.account_id).exists?) return false if should_filter end @@ -248,11 +249,13 @@ class FeedManager check_for_blocks = crutches[:active_mentions][status.id] || [] check_for_blocks.concat([status.account_id]) + check_for_blocks.concat([status.conversation.account_id]) unless status.conversation&.account_id.nil? check_for_blocks.concat([[status.in_reply_to_account_id]]) if status.reply? if status.reblog? check_for_blocks.concat([status.reblog.account_id]) check_for_blocks.concat(crutches[:active_mentions][status.reblog_of_id] || []) + check_for_blocks.concat([status.reblog.conversation.account_id]) unless status.reblog.conversation&.account_id.nil? check_for_blocks.concat([[status.reblog.in_reply_to_account_id]]) if status.reblog.reply? end @@ -260,6 +263,7 @@ class FeedManager if status.reply? && !status.in_reply_to_account_id.nil? # Filter out if it's a reply should_filter = !crutches[:following][status.in_reply_to_account_id] # and I'm not following the person it's a reply to + should_filter &&= !crutches[:following][status.conversation&.account_id] # and I'm not following the thread owner should_filter &&= receiver_id != status.in_reply_to_account_id # and it's not a reply to me should_filter &&= status.account_id != status.in_reply_to_account_id # and it's not a self-reply diff --git a/app/models/concerns/account_associations.rb b/app/models/concerns/account_associations.rb index cca3a17fa..db7396582 100644 --- a/app/models/concerns/account_associations.rb +++ b/app/models/concerns/account_associations.rb @@ -60,5 +60,8 @@ module AccountAssociations # Hashtags has_and_belongs_to_many :tags has_many :featured_tags, -> { includes(:tag) }, dependent: :destroy, inverse_of: :account + + # Threads + has_many :threads, class_name: 'Conversation', inverse_of: :account, dependent: :nullify end end diff --git a/app/models/conversation.rb b/app/models/conversation.rb index bbe3ada31..d1674fe4e 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -7,6 +7,8 @@ # uri :string # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) +# public :boolean default(FALSE), not null # class Conversation < ApplicationRecord @@ -14,6 +16,7 @@ class Conversation < ApplicationRecord has_many :statuses has_many :mutes, class_name: 'ConversationMute', inverse_of: :conversation, dependent: :destroy + belongs_to :account, inverse_of: :threads def local? uri.nil? diff --git a/app/models/status.rb b/app/models/status.rb index 120a69715..657fcaca0 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -290,8 +290,7 @@ class Status < ApplicationRecord before_validation :prepare_contents, if: :local? before_validation :set_reblog - before_validation :set_conversation - before_validation :set_visibility + before_validation :set_conversation_perms before_validation :set_local after_create :set_poll_id @@ -537,27 +536,25 @@ class Status < ApplicationRecord update_column(:poll_id, poll.id) unless poll.nil? end - def set_visibility - self.visibility = reblog.visibility if reblog? && visibility.nil? - self.visibility = (account.locked? ? :private : :public) if visibility.nil? - self.visibility = thread.visibility unless thread.nil? || %w(public unlisted).include?(thread.visibility) || ['direct', 'limited', thread.visibility].include?(visibility.to_s) - self.sensitive = false if sensitive.nil? - end - def set_locality self.local_only = marked_local_only? if account.domain.nil? && !attribute_changed?(:local_only) end - def set_conversation + def set_conversation_perms self.thread = thread.reblog if thread&.reblog? - self.reply = !(in_reply_to_id.nil? && thread.nil?) unless reply + self.visibility = reblog.visibility if reblog? && visibility.nil? + self.visibility = (account.locked? ? :private : :public) if visibility.nil? + self.visibility = thread.visibility unless thread.nil? || %w(public unlisted).include?(thread.visibility) || ['direct', 'limited', thread.visibility].include?(visibility.to_s) + self.sensitive = false if sensitive.nil? if reply? && !thread.nil? self.in_reply_to_account_id = carried_over_reply_to_account_id self.conversation_id = thread.conversation_id if conversation_id.nil? elsif conversation_id.nil? - self.conversation = Conversation.new + self.conversation = reply? ? Conversation.new(account_id: nil, public: false) : Conversation.new(account_id: account_id, public: %w(public unlisted).include?(visibility.to_s)) + elsif !reply? && account_id != conversation.account_id + conversation.update!(account_id: account_id, public: %w(public unlisted).include?(visibility.to_s)) end end @@ -616,4 +613,4 @@ class Status < ApplicationRecord end end end -# rubocop:enable Metrics/ClassLength \ No newline at end of file +# rubocop:enable Metrics/ClassLength diff --git a/app/policies/status_policy.rb b/app/policies/status_policy.rb index 22e985b03..bec58c39f 100644 --- a/app/policies/status_policy.rb +++ b/app/policies/status_policy.rb @@ -18,9 +18,9 @@ class StatusPolicy < ApplicationPolicy if requires_mention? owned? || mention_exists? elsif private? - owned? || (following_author? && following_parent_author?) || mention_exists? + owned? || following_owners? || mention_exists? else - current_account.nil? || !(author_blocking? || parent_author_blocking? || author_blocking_domain? || parent_author_blocking_domain?) + current_account.nil? || !blocked_by_owners? end end @@ -53,7 +53,7 @@ class StatusPolicy < ApplicationPolicy end def private? - record.private_visibility? + record.private_visibility? || !public_conversation? end def mention_exists? @@ -78,6 +78,12 @@ class StatusPolicy < ApplicationPolicy parent_author.domain_blocking?(current_account.domain) end + def conversation_author_blocking_domain? + return false if current_account.nil? || current_account.domain.nil? || conversation_owner.nil? + + conversation_owner.domain_blocking?(current_account.domain) + end + def blocking_author? return false if current_account.nil? @@ -96,6 +102,19 @@ class StatusPolicy < ApplicationPolicy @preloaded_relations[:blocked_by] ? @preloaded_relations[:blocked_by][parent_author.id] : parent_author.blocking?(current_account) end + def conversation_author_blocking? + return public_conversation? if conversation_owner.nil? + + @preloaded_relations[:blocked_by] ? @preloaded_relations[:blocked_by][conversation_owner.id] : conversation_owner.blocking?(current_account) + end + + def blocked_by_owners? + return (author_blocking? || author_blocking_domain?) if conversation_owner&.id == author.id && parent_author&.id == author.id + return true if conversation_author_blocking? || parent_author_blocking? || author_blocking? + + conversation_author_blocking_domain? || parent_author_blocking_domain? || author_blocking_domain? + end + def following_author? return false if current_account.nil? @@ -109,14 +128,31 @@ class StatusPolicy < ApplicationPolicy @preloaded_relations[:following] ? @preloaded_relations[:following][parent_author.id] : current_account.following?(parent_author) end + def following_conversation_owner? + return false if current_account.nil? + return public_conversation? if conversation_owner.nil? + + @preloaded_relations[:following] ? @preloaded_relations[:following][conversation_owner.id] : current_account.following?(conversation_owner) + end + + def following_owners? + return following_author? if conversation_owner&.id == author.id && parent_author&.id == author.id + + following_conversation_owner? && following_parent_author? && following_author? + end + def author - record.account + @author ||= record.account end def parent_author - record.in_reply_to_account + @parent_author ||= record.in_reply_to_account end - + + def conversation_owner + @conversation_owner ||= record.conversation&.account + end + def local_only? record.local_only? end @@ -124,4 +160,8 @@ class StatusPolicy < ApplicationPolicy def published? record.published? end + + def public_conversation? + @public_conversation ||= (record.conversation&.public? || false) + end end -- cgit