From ad892dbc0c377d82b2cae349f189a41708a1e6af Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 17 Aug 2017 21:35:00 +0200 Subject: Add _:atomUri property for deduplicating OStatus/ActivityPub legacy records (#4593) --- app/serializers/activitypub/note_serializer.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'app/serializers/activitypub/note_serializer.rb') diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index 4c13f8e59..bc8eb8a35 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -8,6 +8,8 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer has_many :media_attachments, key: :attachment has_many :virtual_tags, key: :tag + attribute :atom_uri, key: '_:atomUri', if: :local? + def id ActivityPub::TagManager.instance.uri_for(object) end @@ -52,6 +54,14 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer object.mentions + object.tags end + def atom_uri + ::TagManager.instance.uri_for(object) + end + + def local? + object.account.local? + end + class MediaAttachmentSerializer < ActiveModel::Serializer include RoutingHelper -- cgit From 412ea873060da4dc73236fdd63a2931d27dbfa40 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 19 Aug 2017 18:44:48 +0200 Subject: Improve ActivityPub/OStatus compatibility (#4632) *Note: OStatus URIs are invalid for ActivityPub. But we have them for as long as we want to keep old OStatus-sourced content and as long as we remain OStatus-compatible.* - In Announce handling, if object URI is not a URL, fallback to object URL - Do not use specialized ThreadResolveWorker, rely on generalized handling - When serializing notes, if parent's URI is not a URL, use parent's URL --- app/lib/activitypub/activity/announce.rb | 14 ++++++++++++-- app/lib/activitypub/activity/create.rb | 2 +- app/serializers/activitypub/note_serializer.rb | 8 +++++++- app/workers/activitypub/thread_resolve_worker.rb | 17 ----------------- spec/workers/activitypub/thread_resolve_worker_spec.rb | 16 ---------------- 5 files changed, 20 insertions(+), 37 deletions(-) delete mode 100644 app/workers/activitypub/thread_resolve_worker.rb delete mode 100644 spec/workers/activitypub/thread_resolve_worker_spec.rb (limited to 'app/serializers/activitypub/note_serializer.rb') diff --git a/app/lib/activitypub/activity/announce.rb b/app/lib/activitypub/activity/announce.rb index decf8f960..09fec28a0 100644 --- a/app/lib/activitypub/activity/announce.rb +++ b/app/lib/activitypub/activity/announce.rb @@ -2,8 +2,8 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity def perform - original_status = status_from_uri(object_uri) - original_status = ActivityPub::FetchRemoteStatusService.new.call(object_uri) if original_status.nil? + original_status = status_from_uri(object_uri) + original_status ||= fetch_remote_original_status return if original_status.nil? || delete_arrived_first?(@json['id']) @@ -11,4 +11,14 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity distribute(status) status end + + private + + def fetch_remote_original_status + if object_uri.start_with?('http') + ActivityPub::FetchRemoteStatusService.new.call(object_uri) + elsif @object['url'].present? + ::FetchRemoteStatusService.new.call(@object['url']) + end + end end diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 77d66fba3..154125759 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -91,7 +91,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def resolve_thread(status) return unless status.reply? && status.thread.nil? - ActivityPub::ThreadResolveWorker.perform_async(status.id, @object['inReplyTo']) + ThreadResolveWorker.perform_async(status.id, @object['inReplyTo']) end def conversation_from_uri(uri) diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index bc8eb8a35..4061b9ce4 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -27,7 +27,13 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer end def in_reply_to - ActivityPub::TagManager.instance.uri_for(object.thread) if object.reply? + return unless object.reply? + + if object.thread.uri.nil? || object.thread.uri.start_with?('http') + ActivityPub::TagManager.instance.uri_for(object.thread) + else + object.thread.url + end end def published diff --git a/app/workers/activitypub/thread_resolve_worker.rb b/app/workers/activitypub/thread_resolve_worker.rb deleted file mode 100644 index 4ef762d06..000000000 --- a/app/workers/activitypub/thread_resolve_worker.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -class ActivityPub::ThreadResolveWorker - include Sidekiq::Worker - - sidekiq_options queue: 'pull', retry: false - - def perform(child_status_id, parent_uri) - child_status = Status.find(child_status_id) - parent_status = ActivityPub::FetchRemoteStatusService.new.call(parent_uri) - - return if parent_status.nil? - - child_status.thread = parent_status - child_status.save! - end -end diff --git a/spec/workers/activitypub/thread_resolve_worker_spec.rb b/spec/workers/activitypub/thread_resolve_worker_spec.rb deleted file mode 100644 index b954cb62c..000000000 --- a/spec/workers/activitypub/thread_resolve_worker_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'rails_helper' - -describe ActivityPub::ThreadResolveWorker do - subject { described_class.new } - - let(:status) { Fabricate(:status) } - let(:parent) { Fabricate(:status) } - - describe '#perform' do - it 'gets parent from ActivityPub::FetchRemoteStatusService and glues them together' do - allow(ActivityPub::FetchRemoteStatusService).to receive(:new).and_return(double(:service, call: parent)) - subject.perform(status.id, 'http://example.com/123') - expect(status.reload.in_reply_to_id).to eq parent.id - end - end -end -- cgit From 0d5d11eeff048a5022a6eef68d299856f5bb9860 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 26 Aug 2017 19:55:10 +0200 Subject: Add _:inReplyToAtomUri to ActivityPub (#4702) --- app/helpers/jsonld_helper.rb | 2 +- app/lib/activitypub/activity/create.rb | 17 ++++++++++++++--- app/serializers/activitypub/activity_serializer.rb | 6 +++++- app/serializers/activitypub/note_serializer.rb | 7 +++++++ 4 files changed, 27 insertions(+), 5 deletions(-) (limited to 'app/serializers/activitypub/note_serializer.rb') diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb index 09446c8be..d82a07332 100644 --- a/app/helpers/jsonld_helper.rb +++ b/app/helpers/jsonld_helper.rb @@ -10,7 +10,7 @@ module JsonLdHelper end def value_or_id(value) - value.is_a?(String) ? value : value['id'] + value.is_a?(String) || value.nil? ? value : value['id'] end def supported_context?(json) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 5c59c4b24..114aed84f 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -91,7 +91,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def resolve_thread(status) return unless status.reply? && status.thread.nil? - ThreadResolveWorker.perform_async(status.id, @object['inReplyTo']) + ThreadResolveWorker.perform_async(status.id, in_reply_to_uri) end def conversation_from_uri(uri) @@ -118,8 +118,19 @@ class ActivityPub::Activity::Create < ActivityPub::Activity end def replied_to_status - return if @object['inReplyTo'].blank? - @replied_to_status ||= status_from_uri(@object['inReplyTo']) + return @replied_to_status if defined?(@replied_to_status) + + if in_reply_to_uri.blank? + @replied_to_status = nil + else + @replied_to_status = status_from_uri(in_reply_to_uri) + @replied_to_status ||= status_from_uri(@object['_:inReplyToAtomUri']) if @object['_:inReplyToAtomUri'].present? + @replied_to_status + end + end + + def in_reply_to_uri + value_or_id(@object['inReplyTo']) end def text_from_content diff --git a/app/serializers/activitypub/activity_serializer.rb b/app/serializers/activitypub/activity_serializer.rb index d20ee9920..349495e84 100644 --- a/app/serializers/activitypub/activity_serializer.rb +++ b/app/serializers/activitypub/activity_serializer.rb @@ -10,7 +10,7 @@ class ActivityPub::ActivitySerializer < ActiveModel::Serializer end def type - object.reblog? ? 'Announce' : 'Create' + announce? ? 'Announce' : 'Create' end def actor @@ -24,4 +24,8 @@ class ActivityPub::ActivitySerializer < ActiveModel::Serializer def cc ActivityPub::TagManager.instance.cc(object) end + + def announce? + object.reblog? + end end diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index 4061b9ce4..15031dfdc 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -9,6 +9,7 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer has_many :virtual_tags, key: :tag attribute :atom_uri, key: '_:atomUri', if: :local? + attribute :in_reply_to_atom_uri, key: '_:inReplyToAtomUri' def id ActivityPub::TagManager.instance.uri_for(object) @@ -64,6 +65,12 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer ::TagManager.instance.uri_for(object) end + def in_reply_to_atom_uri + return unless object.reply? + + ::TagManager.instance.uri_for(object.thread) + end + def local? object.account.local? end -- cgit From 1b5806b74475213ae45b483d08a29daa40988f84 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 2 Sep 2017 14:01:23 +0200 Subject: Define missing JSON-LD properties (#4767) Using _: property names is discouraged, as in the future, canonicalization may throw an error when encountering that instead of discarding it silently like it does now. We are defining some ActivityStreams properties which we expect to land in ActivityStreams eventually, to ensure that future versions of Mastodon will remain compatible with this even once that happens. Those would be `locked`, `sensitive` and `Hashtag` We are defining a custom context inline for some properties which we do not expect to land in any other context. `atomUri`, `inReplyToAtomUri` and `conversation` are part of the custom defined OStatus context. --- app/lib/activitypub/activity/create.rb | 6 +++--- app/lib/activitypub/activity/delete.rb | 2 +- app/lib/activitypub/adapter.rb | 20 +++++++++++++++++++- app/serializers/activitypub/actor_serializer.rb | 4 +--- app/serializers/activitypub/delete_serializer.rb | 3 +-- app/serializers/activitypub/note_serializer.rb | 17 +++++++++++++---- .../activitypub/fetch_remote_status_service.rb | 4 ++-- app/services/activitypub/process_account_service.rb | 2 +- 8 files changed, 41 insertions(+), 17 deletions(-) (limited to 'app/serializers/activitypub/note_serializer.rb') diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index a6f1e60d9..081e80570 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -26,7 +26,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def find_existing_status status = status_from_uri(object_uri) - status ||= Status.find_by(uri: @object['_:atomUri']) if @object['_:atomUri'].present? + status ||= Status.find_by(uri: @object['atomUri']) if @object['atomUri'].present? status end @@ -43,7 +43,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity sensitive: @object['sensitive'] || false, visibility: visibility_from_audience, thread: replied_to_status, - conversation: conversation_from_uri(@object['_:conversation']), + conversation: conversation_from_uri(@object['conversation']), } end @@ -125,7 +125,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity @replied_to_status = nil else @replied_to_status = status_from_uri(in_reply_to_uri) - @replied_to_status ||= status_from_uri(@object['_:inReplyToAtomUri']) if @object['_:inReplyToAtomUri'].present? + @replied_to_status ||= status_from_uri(@object['inReplyToAtomUri']) if @object['inReplyToAtomUri'].present? @replied_to_status end end diff --git a/app/lib/activitypub/activity/delete.rb b/app/lib/activitypub/activity/delete.rb index 9d804c86d..4c6afb090 100644 --- a/app/lib/activitypub/activity/delete.rb +++ b/app/lib/activitypub/activity/delete.rb @@ -17,7 +17,7 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity def delete_note status = Status.find_by(uri: object_uri, account: @account) - status ||= Status.find_by(uri: @object['_:atomUri'], account: @account) if @object.is_a?(Hash) && @object['_:atomUri'].present? + status ||= Status.find_by(uri: @object['atomUri'], account: @account) if @object.is_a?(Hash) && @object['atomUri'].present? delete_later!(object_uri) diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb index 92210579e..fe4dddd38 100644 --- a/app/lib/activitypub/adapter.rb +++ b/app/lib/activitypub/adapter.rb @@ -1,6 +1,24 @@ # frozen_string_literal: true class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base + CONTEXT = { + '@context': [ + 'https://www.w3.org/ns/activitystreams', + 'https://w3id.org/security/v1', + + { + 'locked' => 'as:locked', + 'sensitive' => 'as:sensitive', + 'Hashtag' => 'as:Hashtag', + + 'ostatus' => 'http://ostatus.org#', + 'atomUri' => 'ostatus:atomUri', + 'inReplyToAtomUri' => 'ostatus:inReplyToAtomUri', + 'conversation' => 'ostatus:conversation', + }, + ], + }.freeze + def self.default_key_transform :camel_lower end @@ -11,7 +29,7 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base def serializable_hash(options = nil) options = serialization_options(options) - serialized_hash = { '@context': [ActivityPub::TagManager::CONTEXT, 'https://w3id.org/security/v1'] }.merge(ActiveModelSerializers::Adapter::Attributes.new(serializer, instance_options).serializable_hash(options)) + serialized_hash = CONTEXT.merge(ActiveModelSerializers::Adapter::Attributes.new(serializer, instance_options).serializable_hash(options)) self.class.transform_key_casing!(serialized_hash, instance_options) end end diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb index a72ecee24..f004dc326 100644 --- a/app/serializers/activitypub/actor_serializer.rb +++ b/app/serializers/activitypub/actor_serializer.rb @@ -6,12 +6,10 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer attributes :id, :type, :following, :followers, :inbox, :outbox, :shared_inbox, :preferred_username, :name, :summary, - :url + :url, :locked has_one :public_key, serializer: ActivityPub::PublicKeySerializer - attribute :locked, key: '_:locked' - class ImageSerializer < ActiveModel::Serializer include RoutingHelper diff --git a/app/serializers/activitypub/delete_serializer.rb b/app/serializers/activitypub/delete_serializer.rb index a041c577b..87a43b95d 100644 --- a/app/serializers/activitypub/delete_serializer.rb +++ b/app/serializers/activitypub/delete_serializer.rb @@ -2,8 +2,7 @@ class ActivityPub::DeleteSerializer < ActiveModel::Serializer class TombstoneSerializer < ActiveModel::Serializer - attributes :id, :type - attribute :atom_uri, key: '_:atomUri' + attributes :id, :type, :atom_uri def id ActivityPub::TagManager.instance.uri_for(object) diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index 15031dfdc..d42f54263 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -3,14 +3,13 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer attributes :id, :type, :summary, :content, :in_reply_to, :published, :url, - :attributed_to, :to, :cc, :sensitive + :attributed_to, :to, :cc, :sensitive, + :atom_uri, :in_reply_to_atom_uri, + :conversation has_many :media_attachments, key: :attachment has_many :virtual_tags, key: :tag - attribute :atom_uri, key: '_:atomUri', if: :local? - attribute :in_reply_to_atom_uri, key: '_:inReplyToAtomUri' - def id ActivityPub::TagManager.instance.uri_for(object) end @@ -62,6 +61,8 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer end def atom_uri + return unless object.local? + ::TagManager.instance.uri_for(object) end @@ -71,6 +72,14 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer ::TagManager.instance.uri_for(object.thread) end + def conversation + if object.conversation.uri? + object.conversation.uri + else + TagManager.instance.unique_tag(object.conversation.created_at, object.conversation.id, 'Conversation') + end + end + def local? object.account.local? end diff --git a/app/services/activitypub/fetch_remote_status_service.rb b/app/services/activitypub/fetch_remote_status_service.rb index c114515cd..68ca58d62 100644 --- a/app/services/activitypub/fetch_remote_status_service.rb +++ b/app/services/activitypub/fetch_remote_status_service.rb @@ -25,8 +25,8 @@ class ActivityPub::FetchRemoteStatusService < BaseService def activity_json if %w(Note Article).include? @json['type'] { - 'type' => 'Create', - 'actor' => first_of_value(@json['attributedTo']), + 'type' => 'Create', + 'actor' => first_of_value(@json['attributedTo']), 'object' => @json, } else diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 99f9dbdc2..44798d051 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -50,7 +50,7 @@ class ActivityPub::ProcessAccountService < BaseService @account.avatar_remote_url = image_url('icon') @account.header_remote_url = image_url('image') @account.public_key = public_key || '' - @account.locked = @json['_:locked'] || false + @account.locked = @json['locked'] || false @account.save! end -- cgit