Skip to content

Commit

Permalink
Alternative handling of private self-boosts (mastodon#9998)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ClearlyClaire authored and hiyuki2578 committed Oct 2, 2019
1 parent 6034720 commit 8a094bd
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 26 deletions.
32 changes: 32 additions & 0 deletions app/lib/activitypub/activity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -71,6 +74,18 @@ def object_uri
@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)

Expand Down Expand Up @@ -120,6 +135,23 @@ def delete_later!(uri)
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)
Expand Down
4 changes: 1 addition & 3 deletions app/lib/activitypub/activity/announce.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
15 changes: 0 additions & 15 deletions app/lib/activitypub/activity/create.rb
Original file line number Diff line number Diff line change
@@ -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'])
Expand Down Expand Up @@ -318,22 +315,10 @@ def name_language_map?
@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?
Expand Down
8 changes: 6 additions & 2 deletions app/serializers/activitypub/activity_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -42,4 +42,8 @@ def atom_uri
def announce?
object.reblog?
end

def owned_announce?
announce? && object.account == object.proper.account && object.proper.private_visibility?
end
end
53 changes: 47 additions & 6 deletions spec/lib/activitypub/activity/announce_spec.rb
Original file line number Diff line number Diff line change
@@ -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) }

Expand All @@ -11,19 +11,60 @@
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) }
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

it 'creates a reblog by sender of status' do
expect(sender.reblogged?(status)).to be true
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

0 comments on commit 8a094bd

Please sign in to comment.