Alternative handling of private self-boosts (#9998)
* When self-boosting, embed original toot into Announce serialization * Process unknown self-boosts from Announce object if it is more than an URI * Add some self-boost specs * Only serialize private toots in self-Announces
This commit is contained in:
		
					parent
					
						
							
								169b9d4428
							
						
					
				
			
			
				commit
				
					
						6a5307a573
					
				
			
		
					 5 changed files with 86 additions and 26 deletions
				
			
		|  | @ -4,6 +4,9 @@ class ActivityPub::Activity | |||
|   include JsonLdHelper | ||||
|   include Redisable | ||||
| 
 | ||||
|   SUPPORTED_TYPES = %w(Note).freeze | ||||
|   CONVERTED_TYPES = %w(Image Video Article Page).freeze | ||||
| 
 | ||||
|   def initialize(json, account, **options) | ||||
|     @json    = json | ||||
|     @account = account | ||||
|  | @ -71,6 +74,18 @@ class ActivityPub::Activity | |||
|     @object_uri ||= value_or_id(@object) | ||||
|   end | ||||
| 
 | ||||
|   def unsupported_object_type? | ||||
|     @object.is_a?(String) || !(supported_object_type? || converted_object_type?) | ||||
|   end | ||||
| 
 | ||||
|   def supported_object_type? | ||||
|     equals_or_includes_any?(@object['type'], SUPPORTED_TYPES) | ||||
|   end | ||||
| 
 | ||||
|   def converted_object_type? | ||||
|     equals_or_includes_any?(@object['type'], CONVERTED_TYPES) | ||||
|   end | ||||
| 
 | ||||
|   def distribute(status) | ||||
|     crawl_links(status) | ||||
| 
 | ||||
|  | @ -120,6 +135,23 @@ class ActivityPub::Activity | |||
|     redis.setex("delete_upon_arrival:#{@account.id}:#{uri}", 6.hours.seconds, uri) | ||||
|   end | ||||
| 
 | ||||
|   def status_from_object | ||||
|     # If the status is already known, return it | ||||
|     status = status_from_uri(object_uri) | ||||
|     return status unless status.nil? | ||||
| 
 | ||||
|     # If the boosted toot is embedded and it is a self-boost, handle it like a Create | ||||
|     unless unsupported_object_type? | ||||
|       actor_id = value_or_id(first_of_value(@object['attributedTo'])) || @account.uri | ||||
|       if actor_id == @account.uri | ||||
|         return ActivityPub::Activity.factory({ 'type' => 'Create', 'actor' => actor_id, 'object' => @object }, @account).perform | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     # If the status is not from the actor, try to fetch it | ||||
|     return fetch_remote_original_status if value_or_id(first_of_value(@json['attributedTo'])) == @account.uri | ||||
|   end | ||||
| 
 | ||||
|   def fetch_remote_original_status | ||||
|     if object_uri.start_with?('http') | ||||
|       return if ActivityPub::TagManager.instance.local_uri?(object_uri) | ||||
|  |  | |||
|  | @ -2,9 +2,7 @@ | |||
| 
 | ||||
| class ActivityPub::Activity::Announce < ActivityPub::Activity | ||||
|   def perform | ||||
|     original_status   = status_from_uri(object_uri) | ||||
|     original_status ||= fetch_remote_original_status | ||||
| 
 | ||||
|     original_status = status_from_object | ||||
|     return if original_status.nil? || delete_arrived_first?(@json['id']) || !announceable?(original_status) | ||||
| 
 | ||||
|     status = Status.find_by(account: @account, reblog: original_status) | ||||
|  |  | |||
|  | @ -1,9 +1,6 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class ActivityPub::Activity::Create < ActivityPub::Activity | ||||
|   SUPPORTED_TYPES = %w(Note).freeze | ||||
|   CONVERTED_TYPES = %w(Image Video Article Page).freeze | ||||
| 
 | ||||
|   def perform | ||||
|     return if unsupported_object_type? || invalid_origin?(@object['id']) | ||||
|     return if Tombstone.exists?(uri: @object['id']) | ||||
|  | @ -318,22 +315,10 @@ class ActivityPub::Activity::Create < ActivityPub::Activity | |||
|     @object['nameMap'].is_a?(Hash) && !@object['nameMap'].empty? | ||||
|   end | ||||
| 
 | ||||
|   def unsupported_object_type? | ||||
|     @object.is_a?(String) || !(supported_object_type? || converted_object_type?) | ||||
|   end | ||||
| 
 | ||||
|   def unsupported_media_type?(mime_type) | ||||
|     mime_type.present? && !(MediaAttachment::IMAGE_MIME_TYPES + MediaAttachment::VIDEO_MIME_TYPES).include?(mime_type) | ||||
|   end | ||||
| 
 | ||||
|   def supported_object_type? | ||||
|     equals_or_includes_any?(@object['type'], SUPPORTED_TYPES) | ||||
|   end | ||||
| 
 | ||||
|   def converted_object_type? | ||||
|     equals_or_includes_any?(@object['type'], CONVERTED_TYPES) | ||||
|   end | ||||
| 
 | ||||
|   def skip_download? | ||||
|     return @skip_download if defined?(@skip_download) | ||||
|     @skip_download ||= DomainBlock.find_by(domain: @account.domain)&.reject_media? | ||||
|  |  | |||
|  | @ -3,8 +3,8 @@ | |||
| class ActivityPub::ActivitySerializer < ActiveModel::Serializer | ||||
|   attributes :id, :type, :actor, :published, :to, :cc | ||||
| 
 | ||||
|   has_one :proper, key: :object, serializer: ActivityPub::NoteSerializer, unless: :announce? | ||||
|   attribute :proper_uri, key: :object, if: :announce? | ||||
|   has_one :proper, key: :object, serializer: ActivityPub::NoteSerializer, unless: :owned_announce? | ||||
|   attribute :proper_uri, key: :object, if: :owned_announce? | ||||
|   attribute :atom_uri, if: :announce? | ||||
| 
 | ||||
|   def id | ||||
|  | @ -42,4 +42,8 @@ class ActivityPub::ActivitySerializer < ActiveModel::Serializer | |||
|   def announce? | ||||
|     object.reblog? | ||||
|   end | ||||
| 
 | ||||
|   def owned_announce? | ||||
|     announce? && object.account == object.proper.account && object.proper.private_visibility? | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| require 'rails_helper' | ||||
| 
 | ||||
| RSpec.describe ActivityPub::Activity::Announce do | ||||
|   let(:sender)    { Fabricate(:account) } | ||||
|   let(:sender)    { Fabricate(:account, followers_url: 'http://example.com/followers') } | ||||
|   let(:recipient) { Fabricate(:account) } | ||||
|   let(:status)    { Fabricate(:status, account: recipient) } | ||||
| 
 | ||||
|  | @ -11,19 +11,60 @@ RSpec.describe ActivityPub::Activity::Announce do | |||
|       id: 'foo', | ||||
|       type: 'Announce', | ||||
|       actor: ActivityPub::TagManager.instance.uri_for(sender), | ||||
|       object: ActivityPub::TagManager.instance.uri_for(status), | ||||
|       object: object_json, | ||||
|     }.with_indifferent_access | ||||
|   end | ||||
| 
 | ||||
|   describe '#perform' do | ||||
|   subject { described_class.new(json, sender) } | ||||
| 
 | ||||
|   before do | ||||
|     sender.update(uri: ActivityPub::TagManager.instance.uri_for(sender)) | ||||
|   end | ||||
| 
 | ||||
|   describe '#perform' do | ||||
|     before do | ||||
|       subject.perform | ||||
|     end | ||||
| 
 | ||||
|     context 'a known status' do | ||||
|       let(:object_json) do | ||||
|         ActivityPub::TagManager.instance.uri_for(status) | ||||
|       end | ||||
| 
 | ||||
|       it 'creates a reblog by sender of status' do | ||||
|         expect(sender.reblogged?(status)).to be true | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'self-boost of a previously unknown status with missing attributedTo' do | ||||
|       let(:object_json) do | ||||
|         { | ||||
|           id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, | ||||
|           type: 'Note', | ||||
|           content: 'Lorem ipsum', | ||||
|           to: 'http://example.com/followers', | ||||
|         } | ||||
|       end | ||||
| 
 | ||||
|       it 'creates a reblog by sender of status' do | ||||
|         expect(sender.reblogged?(sender.statuses.first)).to be true | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'self-boost of a previously unknown status with correct attributedTo' do | ||||
|       let(:object_json) do | ||||
|         { | ||||
|           id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, | ||||
|           type: 'Note', | ||||
|           content: 'Lorem ipsum', | ||||
|           attributedTo: ActivityPub::TagManager.instance.uri_for(sender), | ||||
|           to: 'http://example.com/followers', | ||||
|         } | ||||
|       end | ||||
| 
 | ||||
|       it 'creates a reblog by sender of status' do | ||||
|         expect(sender.reblogged?(sender.statuses.first)).to be true | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue