diff options
Diffstat (limited to 'app/services')
-rw-r--r-- | app/services/activitypub/fetch_remote_account_service.rb | 78 | ||||
-rw-r--r-- | app/services/activitypub/fetch_remote_actor_service.rb | 80 | ||||
-rw-r--r-- | app/services/activitypub/fetch_remote_key_service.rb | 22 | ||||
-rw-r--r-- | app/services/activitypub/process_collection_service.rb | 11 | ||||
-rw-r--r-- | app/services/fetch_resource_service.rb | 2 | ||||
-rw-r--r-- | app/services/keys/claim_service.rb | 2 | ||||
-rw-r--r-- | app/services/resolve_url_service.rb | 4 |
7 files changed, 107 insertions, 92 deletions
diff --git a/app/services/activitypub/fetch_remote_account_service.rb b/app/services/activitypub/fetch_remote_account_service.rb index d7d739c59..ca7a8c6ca 100644 --- a/app/services/activitypub/fetch_remote_account_service.rb +++ b/app/services/activitypub/fetch_remote_account_service.rb @@ -1,80 +1,12 @@ # frozen_string_literal: true -class ActivityPub::FetchRemoteAccountService < BaseService - include JsonLdHelper - include DomainControlHelper - include WebfingerHelper - - class Error < StandardError; end - - SUPPORTED_TYPES = %w(Application Group Organization Person Service).freeze - +class ActivityPub::FetchRemoteAccountService < ActivityPub::FetchRemoteActorService # Does a WebFinger roundtrip on each call, unless `only_key` is true def call(uri, id: true, prefetched_body: nil, break_on_redirect: false, only_key: false, suppress_errors: true) - return if domain_not_allowed?(uri) - return ActivityPub::TagManager.instance.uri_to_resource(uri, Account) if ActivityPub::TagManager.instance.local_uri?(uri) - - @json = begin - if prefetched_body.nil? - fetch_resource(uri, id) - else - body_to_json(prefetched_body, compare_id: id ? uri : nil) - end - rescue Oj::ParseError - raise Error, "Error parsing JSON-LD document #{uri}" - end - - raise Error, "Error fetching actor JSON at #{uri}" if @json.nil? - raise Error, "Unsupported JSON-LD context for document #{uri}" unless supported_context? - raise Error, "Unexpected object type for actor #{uri} (expected any of: #{SUPPORTED_TYPES})" unless expected_type? - raise Error, "Actor #{uri} has moved to #{@json['movedTo']}" if break_on_redirect && @json['movedTo'].present? - - @uri = @json['id'] - @username = @json['preferredUsername'] - @domain = Addressable::URI.parse(@uri).normalized_host - - check_webfinger! unless only_key - - ActivityPub::ProcessAccountService.new.call(@username, @domain, @json, only_key: only_key, verified_webfinger: !only_key) - rescue Error => e - Rails.logger.debug "Fetching account #{uri} failed: #{e.message}" - raise unless suppress_errors - end - - private - - def check_webfinger! - webfinger = webfinger!("acct:#{@username}@#{@domain}") - confirmed_username, confirmed_domain = split_acct(webfinger.subject) - - if @username.casecmp(confirmed_username).zero? && @domain.casecmp(confirmed_domain).zero? - raise Error, "Webfinger response for #{@username}@#{@domain} does not loop back to #{@uri}" if webfinger.link('self', 'href') != @uri - return - end - - webfinger = webfinger!("acct:#{confirmed_username}@#{confirmed_domain}") - @username, @domain = split_acct(webfinger.subject) - - unless confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero? - raise Webfinger::RedirectError, "Too many webfinger redirects for URI #{uri} (stopped at #{@username}@#{@domain})" - end - - raise Error, "Webfinger response for #{@username}@#{@domain} does not loop back to #{@uri}" if webfinger.link('self', 'href') != @uri - rescue Webfinger::RedirectError => e - raise Error, e.message - rescue Webfinger::Error => e - raise Error, "Webfinger error when resolving #{@username}@#{@domain}: #{e.message}" - end - - def split_acct(acct) - acct.gsub(/\Aacct:/, '').split('@') - end - - def supported_context? - super(@json) - end + actor = super + return actor if actor.nil? || actor.is_a?(Account) - def expected_type? - equals_or_includes_any?(@json['type'], SUPPORTED_TYPES) + Rails.logger.debug "Fetching account #{uri} failed: Expected Account, got #{actor.class.name}" + raise Error, "Expected Account, got #{actor.class.name}" unless suppress_errors end end diff --git a/app/services/activitypub/fetch_remote_actor_service.rb b/app/services/activitypub/fetch_remote_actor_service.rb new file mode 100644 index 000000000..17bf2f287 --- /dev/null +++ b/app/services/activitypub/fetch_remote_actor_service.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +class ActivityPub::FetchRemoteActorService < BaseService + include JsonLdHelper + include DomainControlHelper + include WebfingerHelper + + class Error < StandardError; end + + SUPPORTED_TYPES = %w(Application Group Organization Person Service).freeze + + # Does a WebFinger roundtrip on each call, unless `only_key` is true + def call(uri, id: true, prefetched_body: nil, break_on_redirect: false, only_key: false, suppress_errors: true) + return if domain_not_allowed?(uri) + return ActivityPub::TagManager.instance.uri_to_actor(uri) if ActivityPub::TagManager.instance.local_uri?(uri) + + @json = begin + if prefetched_body.nil? + fetch_resource(uri, id) + else + body_to_json(prefetched_body, compare_id: id ? uri : nil) + end + rescue Oj::ParseError + raise Error, "Error parsing JSON-LD document #{uri}" + end + + raise Error, "Error fetching actor JSON at #{uri}" if @json.nil? + raise Error, "Unsupported JSON-LD context for document #{uri}" unless supported_context? + raise Error, "Unexpected object type for actor #{uri} (expected any of: #{SUPPORTED_TYPES})" unless expected_type? + raise Error, "Actor #{uri} has moved to #{@json['movedTo']}" if break_on_redirect && @json['movedTo'].present? + + @uri = @json['id'] + @username = @json['preferredUsername'] + @domain = Addressable::URI.parse(@uri).normalized_host + + check_webfinger! unless only_key + + ActivityPub::ProcessAccountService.new.call(@username, @domain, @json, only_key: only_key, verified_webfinger: !only_key) + rescue Error => e + Rails.logger.debug "Fetching actor #{uri} failed: #{e.message}" + raise unless suppress_errors + end + + private + + def check_webfinger! + webfinger = webfinger!("acct:#{@username}@#{@domain}") + confirmed_username, confirmed_domain = split_acct(webfinger.subject) + + if @username.casecmp(confirmed_username).zero? && @domain.casecmp(confirmed_domain).zero? + raise Error, "Webfinger response for #{@username}@#{@domain} does not loop back to #{@uri}" if webfinger.link('self', 'href') != @uri + return + end + + webfinger = webfinger!("acct:#{confirmed_username}@#{confirmed_domain}") + @username, @domain = split_acct(webfinger.subject) + + unless confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero? + raise Webfinger::RedirectError, "Too many webfinger redirects for URI #{uri} (stopped at #{@username}@#{@domain})" + end + + raise Error, "Webfinger response for #{@username}@#{@domain} does not loop back to #{@uri}" if webfinger.link('self', 'href') != @uri + rescue Webfinger::RedirectError => e + raise Error, e.message + rescue Webfinger::Error => e + raise Error, "Webfinger error when resolving #{@username}@#{@domain}: #{e.message}" + end + + def split_acct(acct) + acct.gsub(/\Aacct:/, '').split('@') + end + + def supported_context? + super(@json) + end + + def expected_type? + equals_or_includes_any?(@json['type'], SUPPORTED_TYPES) + end +end diff --git a/app/services/activitypub/fetch_remote_key_service.rb b/app/services/activitypub/fetch_remote_key_service.rb index 01008d883..fe8f60b55 100644 --- a/app/services/activitypub/fetch_remote_key_service.rb +++ b/app/services/activitypub/fetch_remote_key_service.rb @@ -5,7 +5,7 @@ class ActivityPub::FetchRemoteKeyService < BaseService class Error < StandardError; end - # Returns account that owns the key + # Returns actor that owns the key def call(uri, id: true, prefetched_body: nil, suppress_errors: true) raise Error, 'No key URI given' if uri.blank? @@ -27,7 +27,7 @@ class ActivityPub::FetchRemoteKeyService < BaseService raise Error, "Unable to fetch key JSON at #{uri}" if @json.nil? raise Error, "Unsupported JSON-LD context for document #{uri}" unless supported_context?(@json) raise Error, "Unexpected object type for key #{uri}" unless expected_type? - return find_account(@json['id'], @json, suppress_errors) if person? + return find_actor(@json['id'], @json, suppress_errors) if person? @owner = fetch_resource(owner_uri, true) @@ -36,7 +36,7 @@ class ActivityPub::FetchRemoteKeyService < BaseService raise Error, "Unexpected object type for actor #{owner_uri} (expected any of: #{SUPPORTED_TYPES})" unless expected_owner_type? raise Error, "publicKey id for #{owner_uri} does not correspond to #{@json['id']}" unless confirmed_owner? - find_account(owner_uri, @owner, suppress_errors) + find_actor(owner_uri, @owner, suppress_errors) rescue Error => e Rails.logger.debug "Fetching key #{uri} failed: #{e.message}" raise unless suppress_errors @@ -44,18 +44,18 @@ class ActivityPub::FetchRemoteKeyService < BaseService private - def find_account(uri, prefetched_body, suppress_errors) - account = ActivityPub::TagManager.instance.uri_to_resource(uri, Account) - account ||= ActivityPub::FetchRemoteAccountService.new.call(uri, prefetched_body: prefetched_body, suppress_errors: suppress_errors) - account + def find_actor(uri, prefetched_body, suppress_errors) + actor = ActivityPub::TagManager.instance.uri_to_actor(uri) + actor ||= ActivityPub::FetchRemoteActorService.new.call(uri, prefetched_body: prefetched_body, suppress_errors: suppress_errors) + actor end def expected_type? - person? || public_key? + actor? || public_key? end - def person? - equals_or_includes_any?(@json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) + def actor? + equals_or_includes_any?(@json['type'], ActivityPub::FetchRemoteActorService::SUPPORTED_TYPES) end def public_key? @@ -67,7 +67,7 @@ class ActivityPub::FetchRemoteKeyService < BaseService end def expected_owner_type? - equals_or_includes_any?(@owner['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) + equals_or_includes_any?(@owner['type'], ActivityPub::FetchRemoteActorService::SUPPORTED_TYPES) end def confirmed_owner? diff --git a/app/services/activitypub/process_collection_service.rb b/app/services/activitypub/process_collection_service.rb index eb008c40a..fffe30195 100644 --- a/app/services/activitypub/process_collection_service.rb +++ b/app/services/activitypub/process_collection_service.rb @@ -3,8 +3,8 @@ class ActivityPub::ProcessCollectionService < BaseService include JsonLdHelper - def call(body, account, **options) - @account = account + def call(body, actor, **options) + @account = actor @json = original_json = Oj.load(body, mode: :strict) @options = options @@ -16,6 +16,7 @@ class ActivityPub::ProcessCollectionService < BaseService end return if !supported_context? || (different_actor? && verify_account!.nil?) || suspended_actor? || @account.local? + return unless @account.is_a?(Account) if @json['signature'].present? # We have verified the signature, but in the compaction step above, might @@ -66,8 +67,10 @@ class ActivityPub::ProcessCollectionService < BaseService end def verify_account! - @options[:relayed_through_account] = @account - @account = ActivityPub::LinkedDataSignature.new(@json).verify_account! + @options[:relayed_through_actor] = @account + @account = ActivityPub::LinkedDataSignature.new(@json).verify_actor! + @account = nil unless @account.is_a?(Account) + @account rescue JSON::LD::JsonLdError => e Rails.logger.debug "Could not verify LD-Signature for #{value_or_id(@json['actor'])}: #{e.message}" nil diff --git a/app/services/fetch_resource_service.rb b/app/services/fetch_resource_service.rb index 6c0093cd4..73204e55d 100644 --- a/app/services/fetch_resource_service.rb +++ b/app/services/fetch_resource_service.rb @@ -47,7 +47,7 @@ class FetchResourceService < BaseService body = response.body_with_limit json = body_to_json(body) - [json['id'], { prefetched_body: body, id: true }] if supported_context?(json) && (equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) || expected_type?(json)) + [json['id'], { prefetched_body: body, id: true }] if supported_context?(json) && (equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteActorService::SUPPORTED_TYPES) || expected_type?(json)) elsif !terminal link_header = response['Link'] && parse_link_header(response) diff --git a/app/services/keys/claim_service.rb b/app/services/keys/claim_service.rb index 69568a0d1..ae9e24a24 100644 --- a/app/services/keys/claim_service.rb +++ b/app/services/keys/claim_service.rb @@ -72,7 +72,7 @@ class Keys::ClaimService < BaseService def build_post_request(uri) Request.new(:post, uri).tap do |request| - request.on_behalf_of(@source_account, :uri) + request.on_behalf_of(@source_account) request.add_headers(HEADERS) end end diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb index e2c745673..37c856cf8 100644 --- a/app/services/resolve_url_service.rb +++ b/app/services/resolve_url_service.rb @@ -20,8 +20,8 @@ class ResolveURLService < BaseService private def process_url - if equals_or_includes_any?(type, ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) - ActivityPub::FetchRemoteAccountService.new.call(resource_url, prefetched_body: body) + if equals_or_includes_any?(type, ActivityPub::FetchRemoteActorService::SUPPORTED_TYPES) + ActivityPub::FetchRemoteActorService.new.call(resource_url, prefetched_body: body) elsif equals_or_includes_any?(type, ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES) status = FetchRemoteStatusService.new.call(resource_url, body) authorize_with @on_behalf_of, status, :show? unless status.nil? |