2022-09-21 20:45:57 +00:00
# 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
2022-12-06 23:15:24 +00:00
def call ( uri , id : true , prefetched_body : nil , break_on_redirect : false , only_key : false , suppress_errors : true , request_id : nil )
2022-09-21 20:45:57 +00:00
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?
2023-02-22 00:57:56 +00:00
raise Error , " Actor #{ uri } has no 'preferredUsername', which is a requirement for Mastodon compatibility " if @json [ 'preferredUsername' ] . blank?
2022-09-21 20:45:57 +00:00
@uri = @json [ 'id' ]
@username = @json [ 'preferredUsername' ]
@domain = Addressable :: URI . parse ( @uri ) . normalized_host
check_webfinger! unless only_key
2022-12-06 23:15:24 +00:00
ActivityPub :: ProcessAccountService . new . call ( @username , @domain , @json , only_key : only_key , verified_webfinger : ! only_key , request_id : request_id )
2022-09-21 20:45:57 +00:00
rescue Error = > e
2023-02-07 02:44:36 +00:00
Rails . logger . debug { " Fetching actor #{ uri } failed: #{ e . message } " }
2022-09-21 20:45:57 +00:00
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
2023-02-20 05:58:28 +00:00
2022-09-21 20:45:57 +00:00
return
end
webfinger = webfinger! ( " acct: #{ confirmed_username } @ #{ confirmed_domain } " )
@username , @domain = split_acct ( webfinger . subject )
2023-02-18 11:37:47 +00:00
raise Webfinger :: RedirectError , " Too many webfinger redirects for URI #{ @uri } (stopped at #{ @username } @ #{ @domain } ) " unless confirmed_username . casecmp ( @username ) . zero? && confirmed_domain . casecmp ( @domain ) . zero?
2022-09-21 20:45:57 +00:00
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 )
2023-05-02 19:07:45 +00:00
acct . delete_prefix ( 'acct:' ) . split ( '@' )
2022-09-21 20:45:57 +00:00
end
def supported_context?
super ( @json )
end
def expected_type?
equals_or_includes_any? ( @json [ 'type' ] , SUPPORTED_TYPES )
end
end