From dd7ef0dc41584089a97444d8192bc61505108e6c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 8 Aug 2017 21:52:15 +0200 Subject: Add ActivityPub inbox (#4216) * Add ActivityPub inbox * Handle ActivityPub deletes * Handle ActivityPub creates * Handle ActivityPub announces * Stubs for handling all activities that need to be handled * Add ActivityPub actor resolving * Handle conversation URI passing in ActivityPub * Handle content language in ActivityPub * Send accept header when fetching actor, handle JSON parse errors * Test for ActivityPub::FetchRemoteAccountService * Handle public key and icon/image when embedded/as array/as resolvable URI * Implement ActivityPub::FetchRemoteStatusService * Add stubs for more interactions * Undo activities implemented * Handle out of order activities * Hook up ActivityPub to ResolveRemoteAccountService, handle Update Account activities * Add fragment IDs to all transient activity serializers * Add tests and fixes * Add stubs for missing tests * Add more tests * Add more tests --- .../activitypub/fetch_remote_status_service.rb | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 app/services/activitypub/fetch_remote_status_service.rb (limited to 'app/services/activitypub/fetch_remote_status_service.rb') diff --git a/app/services/activitypub/fetch_remote_status_service.rb b/app/services/activitypub/fetch_remote_status_service.rb new file mode 100644 index 000000000..80305c53d --- /dev/null +++ b/app/services/activitypub/fetch_remote_status_service.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class ActivityPub::FetchRemoteStatusService < BaseService + include JsonLdHelper + + # Should be called when uri has already been checked for locality + def call(uri) + @json = fetch_resource(uri) + + return unless supported_context? && expected_type? + + attributed_to = first_of_value(@json['attributedTo']) + attributed_to = attributed_to['id'] if attributed_to.is_a?(Hash) + + return unless trustworthy_attribution?(uri, attributed_to) + + actor = ActivityPub::TagManager.instance.uri_to_resource(attributed_to, Account) + actor = ActivityPub::FetchRemoteAccountService.new.call(attributed_to) if actor.nil? + + ActivityPub::Activity::Create.new({ 'object' => @json }, actor).perform + end + + private + + def trustworthy_attribution?(uri, attributed_to) + Addressable::URI.parse(uri).normalized_host.casecmp(Addressable::URI.parse(attributed_to).normalized_host).zero? + end + + def supported_context? + super(@json) + end + + def expected_type? + %w(Note Article).include? @json['type'] + end +end -- cgit From 4e75f0d88932511ad154773f4c77a485367ed36c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 14 Aug 2017 02:29:36 +0200 Subject: Hook up URL-based resource look-up to ActivityPub (#4589) --- app/helpers/jsonld_helper.rb | 8 ++- .../activitypub/fetch_remote_account_service.rb | 4 +- .../activitypub/fetch_remote_status_service.rb | 4 +- app/services/fetch_atom_service.rb | 72 ++++++++++++++-------- app/services/fetch_remote_account_service.rb | 15 +++-- app/services/fetch_remote_status_service.rb | 15 +++-- .../api/subscriptions_controller_spec.rb | 26 ++++---- spec/services/process_feed_service_spec.rb | 5 +- 8 files changed, 92 insertions(+), 57 deletions(-) (limited to 'app/services/activitypub/fetch_remote_status_service.rb') diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb index b0db025bc..c750a7038 100644 --- a/app/helpers/jsonld_helper.rb +++ b/app/helpers/jsonld_helper.rb @@ -16,7 +16,11 @@ module JsonLdHelper def fetch_resource(uri) response = build_request(uri).perform return if response.code != 200 - Oj.load(response.to_s, mode: :strict) + body_to_json(response.to_s) + end + + def body_to_json(body) + body.nil? ? nil : Oj.load(body, mode: :strict) rescue Oj::ParseError nil end @@ -25,7 +29,7 @@ module JsonLdHelper def build_request(uri) request = Request.new(:get, uri) - request.add_headers('Accept' => 'application/activity+json') + request.add_headers('Accept' => 'application/activity+json, application/ld+json') request end end diff --git a/app/services/activitypub/fetch_remote_account_service.rb b/app/services/activitypub/fetch_remote_account_service.rb index e443b9463..3eeca585e 100644 --- a/app/services/activitypub/fetch_remote_account_service.rb +++ b/app/services/activitypub/fetch_remote_account_service.rb @@ -5,8 +5,8 @@ class ActivityPub::FetchRemoteAccountService < BaseService # Should be called when uri has already been checked for locality # Does a WebFinger roundtrip on each call - def call(uri) - @json = fetch_resource(uri) + def call(uri, prefetched_json = nil) + @json = body_to_json(prefetched_json) || fetch_resource(uri) return unless supported_context? && expected_type? diff --git a/app/services/activitypub/fetch_remote_status_service.rb b/app/services/activitypub/fetch_remote_status_service.rb index 80305c53d..993e5389c 100644 --- a/app/services/activitypub/fetch_remote_status_service.rb +++ b/app/services/activitypub/fetch_remote_status_service.rb @@ -4,8 +4,8 @@ class ActivityPub::FetchRemoteStatusService < BaseService include JsonLdHelper # Should be called when uri has already been checked for locality - def call(uri) - @json = fetch_resource(uri) + def call(uri, prefetched_json = nil) + @json = body_to_json(prefetched_json) || fetch_resource(uri) return unless supported_context? && expected_type? diff --git a/app/services/fetch_atom_service.rb b/app/services/fetch_atom_service.rb index 3ac441e3e..c6a4dc2e9 100644 --- a/app/services/fetch_atom_service.rb +++ b/app/services/fetch_atom_service.rb @@ -4,18 +4,10 @@ class FetchAtomService < BaseService def call(url) return if url.blank? - response = Request.new(:head, url).perform + @url = url - Rails.logger.debug "Remote status HEAD request returned code #{response.code}" - - response = Request.new(:get, url).perform if response.code == 405 - - Rails.logger.debug "Remote status GET request returned code #{response.code}" - - return nil if response.code != 200 - return [url, fetch(url)] if response.mime_type == 'application/atom+xml' - return process_headers(url, response) if response['Link'].present? - process_html(fetch(url)) + perform_request + process_response rescue OpenSSL::SSL::SSLError => e Rails.logger.debug "SSL error: #{e}" nil @@ -26,27 +18,57 @@ class FetchAtomService < BaseService private - def process_html(body) - Rails.logger.debug 'Processing HTML' + def perform_request + @response = Request.new(:get, @url) + .add_headers('Accept' => 'application/activity+json, application/ld+json, application/atom+xml, text/html') + .perform + end - page = Nokogiri::HTML(body) - alternate_link = page.xpath('//link[@rel="alternate"]').find { |link| link['type'] == 'application/atom+xml' } + def process_response(terminal = false) + return nil if @response.code != 200 - return nil if alternate_link.nil? - [alternate_link['href'], fetch(alternate_link['href'])] + if @response.mime_type == 'application/atom+xml' + [@url, @response.to_s, :ostatus] + elsif ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(@response.mime_type) + [@url, @response.to_s, :activitypub] + elsif @response['Link'] && !terminal + process_headers + elsif @response.mime_type == 'text/html' && !terminal + process_html + end end - def process_headers(url, response) - Rails.logger.debug 'Processing link header' + def process_html + page = Nokogiri::HTML(@response.to_s) - link_header = LinkHeader.parse(response['Link'].is_a?(Array) ? response['Link'].first : response['Link']) - alternate_link = link_header.find_link(%w(rel alternate), %w(type application/atom+xml)) + json_link = page.xpath('//link[@rel="alternate"]').find { |link| ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(link['type']) } + atom_link = page.xpath('//link[@rel="alternate"]').find { |link| link['type'] == 'application/atom+xml' } - return process_html(fetch(url)) if alternate_link.nil? - [alternate_link.href, fetch(alternate_link.href)] + if !json_link.nil? + @url = json_link['href'] + perform_request + process_response(true) + elsif !atom_link.nil? + @url = atom_link['href'] + perform_request + process_response(true) + end end - def fetch(url) - Request.new(:get, url).perform.to_s + def process_headers + link_header = LinkHeader.parse(@response['Link'].is_a?(Array) ? @response['Link'].first : @response['Link']) + + json_link = link_header.find_link(%w(rel alternate), %w(type application/activity+json)) || link_header.find_link(%w(rel alternate), ['type', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"']) + atom_link = link_header.find_link(%w(rel alternate), %w(type application/atom+xml)) + + if !json_link.nil? + @url = json_link.href + perform_request + process_response(true) + elsif !atom_link.nil? + @url = atom_link.href + perform_request + process_response(true) + end end end diff --git a/app/services/fetch_remote_account_service.rb b/app/services/fetch_remote_account_service.rb index 8eed0d454..41b5374b4 100644 --- a/app/services/fetch_remote_account_service.rb +++ b/app/services/fetch_remote_account_service.rb @@ -5,14 +5,19 @@ class FetchRemoteAccountService < BaseService def call(url, prefetched_body = nil) if prefetched_body.nil? - atom_url, body = FetchAtomService.new.call(url) + resource_url, body, protocol = FetchAtomService.new.call(url) else - atom_url = url - body = prefetched_body + resource_url = url + body = prefetched_body + protocol = :ostatus end - return nil if atom_url.nil? - process_atom(atom_url, body) + case protocol + when :ostatus + process_atom(resource_url, body) + when :activitypub + ActivityPub::FetchRemoteAccountService.new.call(resource_url, body) + end end private diff --git a/app/services/fetch_remote_status_service.rb b/app/services/fetch_remote_status_service.rb index b9f5f97b1..30d8d2538 100644 --- a/app/services/fetch_remote_status_service.rb +++ b/app/services/fetch_remote_status_service.rb @@ -5,14 +5,19 @@ class FetchRemoteStatusService < BaseService def call(url, prefetched_body = nil) if prefetched_body.nil? - atom_url, body = FetchAtomService.new.call(url) + resource_url, body, protocol = FetchAtomService.new.call(url) else - atom_url = url - body = prefetched_body + resource_url = url + body = prefetched_body + protocol = :ostatus end - return nil if atom_url.nil? - process_atom(atom_url, body) + case protocol + when :ostatus + process_atom(resource_url, body) + when :activitypub + ActivityPub::FetchRemoteStatusService.new.call(resource_url, body) + end end private diff --git a/spec/controllers/api/subscriptions_controller_spec.rb b/spec/controllers/api/subscriptions_controller_spec.rb index 76f9740ca..d90da9e32 100644 --- a/spec/controllers/api/subscriptions_controller_spec.rb +++ b/spec/controllers/api/subscriptions_controller_spec.rb @@ -38,19 +38,19 @@ RSpec.describe Api::SubscriptionsController, type: :controller do before do stub_request(:post, "https://quitter.no/main/push/hub").to_return(:status => 200, :body => "", :headers => {}) stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt')) - stub_request(:head, "https://quitter.no/notice/1269244").to_return(status: 404) - stub_request(:head, "https://quitter.no/notice/1265331").to_return(status: 404) - stub_request(:head, "https://community.highlandarrow.com/notice/54411").to_return(status: 404) - stub_request(:head, "https://community.highlandarrow.com/notice/53857").to_return(status: 404) - stub_request(:head, "https://community.highlandarrow.com/notice/51852").to_return(status: 404) - stub_request(:head, "https://social.umeahackerspace.se/notice/424348").to_return(status: 404) - stub_request(:head, "https://community.highlandarrow.com/notice/50467").to_return(status: 404) - stub_request(:head, "https://quitter.no/notice/1243309").to_return(status: 404) - stub_request(:head, "https://quitter.no/user/7477").to_return(status: 404) - stub_request(:head, "https://community.highlandarrow.com/user/1").to_return(status: 404) - stub_request(:head, "https://social.umeahackerspace.se/user/2").to_return(status: 404) - stub_request(:head, "https://gs.kawa-kun.com/user/2").to_return(status: 404) - stub_request(:head, "https://mastodon.social/users/Gargron").to_return(status: 404) + stub_request(:get, "https://quitter.no/notice/1269244").to_return(status: 404) + stub_request(:get, "https://quitter.no/notice/1265331").to_return(status: 404) + stub_request(:get, "https://community.highlandarrow.com/notice/54411").to_return(status: 404) + stub_request(:get, "https://community.highlandarrow.com/notice/53857").to_return(status: 404) + stub_request(:get, "https://community.highlandarrow.com/notice/51852").to_return(status: 404) + stub_request(:get, "https://social.umeahackerspace.se/notice/424348").to_return(status: 404) + stub_request(:get, "https://community.highlandarrow.com/notice/50467").to_return(status: 404) + stub_request(:get, "https://quitter.no/notice/1243309").to_return(status: 404) + stub_request(:get, "https://quitter.no/user/7477").to_return(status: 404) + stub_request(:any, "https://community.highlandarrow.com/user/1").to_return(status: 404) + stub_request(:any, "https://social.umeahackerspace.se/user/2").to_return(status: 404) + stub_request(:any, "https://gs.kawa-kun.com/user/2").to_return(status: 404) + stub_request(:any, "https://mastodon.social/users/Gargron").to_return(status: 404) request.env['HTTP_X_HUB_SIGNATURE'] = "sha1=#{OpenSSL::HMAC.hexdigest('sha1', 'abc', feed)}" request.env['RAW_POST_DATA'] = feed diff --git a/spec/services/process_feed_service_spec.rb b/spec/services/process_feed_service_spec.rb index 5e34370ee..aca675dc6 100644 --- a/spec/services/process_feed_service_spec.rb +++ b/spec/services/process_feed_service_spec.rb @@ -124,8 +124,7 @@ RSpec.describe ProcessFeedService do XML - stub_request(:head, 'https://overwatch.com/users/tracer/updates/1').to_return(status: 200, headers: { 'Content-Type' => 'application/atom+xml' }) - stub_request(:get, 'https://overwatch.com/users/tracer/updates/1').to_return(status: 200, body: real_body) + stub_request(:get, 'https://overwatch.com/users/tracer/updates/1').to_return(status: 200, body: real_body, headers: { 'Content-Type' => 'application/atom+xml' }) bad_actor = Fabricate(:account, username: 'sombra', domain: 'talon.xyz') @@ -168,7 +167,7 @@ XML end it 'ignores reblogs if it failed to retreive reblogged statuses' do - stub_request(:head, 'https://overwatch.com/users/tracer/updates/1').to_return(status: 404) + stub_request(:get, 'https://overwatch.com/users/tracer/updates/1').to_return(status: 404) actor = Fabricate(:account, username: 'tracer', domain: 'overwatch.com') -- cgit From b01a19fe392e0dd16d6b3da3f0b56369f7837cc9 Mon Sep 17 00:00:00 2001 From: unarist Date: Thu, 24 Aug 2017 23:21:42 +0900 Subject: Fetch reblogs as Announce activity instead of Note object (#4672) * Process Create / Announce activity in FetchRemoteStatusService * Use activity URL in ActivityPub for reblogs * Redirect to the original status on StatusesController#show --- app/controllers/statuses_controller.rb | 5 ++ app/lib/activitypub/tag_manager.rb | 8 +++ app/serializers/activitypub/activity_serializer.rb | 2 +- .../activitypub/fetch_remote_status_service.rb | 30 ++++++--- spec/controllers/statuses_controller_spec.rb | 12 ++++ .../fetch_remote_status_service_spec.rb | 72 +++++++++++++++++++++- 6 files changed, 118 insertions(+), 11 deletions(-) (limited to 'app/services/activitypub/fetch_remote_status_service.rb') diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index aa24f23c9..a9768d092 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -9,6 +9,7 @@ class StatusesController < ApplicationController before_action :set_status before_action :set_link_headers before_action :check_account_suspension + before_action :redirect_to_original, only: [:show] def show respond_to do |format| @@ -58,4 +59,8 @@ class StatusesController < ApplicationController def check_account_suspension gone if @account.suspended? end + + def redirect_to_original + redirect_to ::TagManager.instance.url_for(@status.reblog) if @status.reblog? + end end diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index 3c16006cb..de575d9e6 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -19,6 +19,7 @@ class ActivityPub::TagManager when :person short_account_url(target) when :note, :comment, :activity + return activity_account_status_url(target.account, target) if target.reblog? short_account_status_url(target.account, target) end end @@ -30,10 +31,17 @@ class ActivityPub::TagManager when :person account_url(target) when :note, :comment, :activity + return activity_account_status_url(target.account, target) if target.reblog? account_status_url(target.account, target) end end + def activity_uri_for(target) + return nil unless %i(note comment activity).include?(target.object_type) && target.local? + + activity_account_status_url(target.account, target) + end + # Primary audience of a status # Public statuses go out to primarily the public collection # Unlisted and private statuses go out primarily to the followers collection diff --git a/app/serializers/activitypub/activity_serializer.rb b/app/serializers/activitypub/activity_serializer.rb index 69e2160c5..d20ee9920 100644 --- a/app/serializers/activitypub/activity_serializer.rb +++ b/app/serializers/activitypub/activity_serializer.rb @@ -6,7 +6,7 @@ class ActivityPub::ActivitySerializer < ActiveModel::Serializer has_one :proper, key: :object, serializer: ActivityPub::NoteSerializer def id - [ActivityPub::TagManager.instance.uri_for(object), '/activity'].join + [ActivityPub::TagManager.instance.activity_uri_for(object)].join end def type diff --git a/app/services/activitypub/fetch_remote_status_service.rb b/app/services/activitypub/fetch_remote_status_service.rb index 993e5389c..c114515cd 100644 --- a/app/services/activitypub/fetch_remote_status_service.rb +++ b/app/services/activitypub/fetch_remote_status_service.rb @@ -7,21 +7,33 @@ class ActivityPub::FetchRemoteStatusService < BaseService def call(uri, prefetched_json = nil) @json = body_to_json(prefetched_json) || fetch_resource(uri) - return unless supported_context? && expected_type? + return unless supported_context? - attributed_to = first_of_value(@json['attributedTo']) - attributed_to = attributed_to['id'] if attributed_to.is_a?(Hash) + activity = activity_json + actor_id = value_or_id(activity['actor']) - return unless trustworthy_attribution?(uri, attributed_to) + return unless expected_type?(activity) && trustworthy_attribution?(uri, actor_id) - actor = ActivityPub::TagManager.instance.uri_to_resource(attributed_to, Account) - actor = ActivityPub::FetchRemoteAccountService.new.call(attributed_to) if actor.nil? + actor = ActivityPub::TagManager.instance.uri_to_resource(actor_id, Account) + actor = ActivityPub::FetchRemoteAccountService.new.call(actor_id) if actor.nil? - ActivityPub::Activity::Create.new({ 'object' => @json }, actor).perform + ActivityPub::Activity.factory(activity, actor).perform end private + def activity_json + if %w(Note Article).include? @json['type'] + { + 'type' => 'Create', + 'actor' => first_of_value(@json['attributedTo']), + 'object' => @json, + } + else + @json + end + end + def trustworthy_attribution?(uri, attributed_to) Addressable::URI.parse(uri).normalized_host.casecmp(Addressable::URI.parse(attributed_to).normalized_host).zero? end @@ -30,7 +42,7 @@ class ActivityPub::FetchRemoteStatusService < BaseService super(@json) end - def expected_type? - %w(Note Article).include? @json['type'] + def expected_type?(json) + %w(Create Announce).include? json['type'] end end diff --git a/spec/controllers/statuses_controller_spec.rb b/spec/controllers/statuses_controller_spec.rb index 88d365624..95fb4d594 100644 --- a/spec/controllers/statuses_controller_spec.rb +++ b/spec/controllers/statuses_controller_spec.rb @@ -30,6 +30,18 @@ describe StatusesController do end end + context 'status is a reblog' do + it 'redirects to the original status' do + original_account = Fabricate(:account, domain: 'example.com') + original_status = Fabricate(:status, account: original_account, uri: 'tag:example.com,2017:foo', url: 'https://example.com/123') + status = Fabricate(:status, reblog: original_status) + + get :show, params: { account_username: status.account.username, id: status.id } + + expect(response).to redirect_to(original_status.url) + end + end + context 'account is not suspended and status is permitted' do it 'assigns @account' do status = Fabricate(:status) diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb index 47a33b6cb..3b22257ed 100644 --- a/spec/services/activitypub/fetch_remote_status_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb @@ -1,5 +1,75 @@ require 'rails_helper' RSpec.describe ActivityPub::FetchRemoteStatusService do - pending + let(:sender) { Fabricate(:account) } + let(:recipient) { Fabricate(:account) } + let(:valid_domain) { Rails.configuration.x.local_domain } + + let(:note) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: "https://#{valid_domain}/@foo/1234", + type: 'Note', + content: 'Lorem ipsum', + attributedTo: ActivityPub::TagManager.instance.uri_for(sender), + } + end + + let(:create) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: "https://#{valid_domain}/@foo/1234/activity", + type: 'Create', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: note, + } + end + + subject { described_class.new } + + describe '#call' do + before do + subject.call(object[:id], Oj.dump(object)) + end + + context 'with Note object' do + let(:object) { note } + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.text).to eq 'Lorem ipsum' + end + end + + context 'with Create activity' do + let(:object) { create } + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.text).to eq 'Lorem ipsum' + end + end + + context 'with Announce activity' do + let(:status) { Fabricate(:status, account: recipient) } + + let(:object) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: "https://#{valid_domain}/@foo/1234/activity", + type: 'Announce', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: 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 + end 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/services/activitypub/fetch_remote_status_service.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