forked from asonix/mastodon
* Add a more descriptive PrivateNetworkAddressError exception class * Remove unnecessary exception class to rescue clause * Remove unnecessary include to JsonLdHelper * Give more neutral error message when too many webfinger redirects * Remove unnecessary guard condition * Rework how “ActivityPub::FetchRemoteAccountService” handles errors Add “suppress_errors” keyword argument to avoid raising errors in ActivityPub::FetchRemoteAccountService#call (default/previous behavior). * Rework how “ActivityPub::FetchRemoteKeyService” handles errors Add “suppress_errors” keyword argument to avoid raising errors in ActivityPub::FetchRemoteKeyService#call (default/previous behavior). * Fix Webfinger::RedirectError not being a subclass of Webfinger::Error * Add suppress_errors option to ResolveAccountService Defaults to true (to preserve previous behavior). If set to false, errors will be raised instead of caught, allowing the caller to be informed of what went wrong. * Return more precise error when failing to fetch account signing AP payloads * Add tests * Fixes * Refactor error handling a bit * Fix various issues * Add specific error when provided Digest is not 256 bits of base64-encoded data * Please CodeClimate * Improve webfinger error reporting
116 lines
2.9 KiB
Ruby
116 lines
2.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Webfinger
|
|
class Error < StandardError; end
|
|
class GoneError < Error; end
|
|
class RedirectError < Error; end
|
|
|
|
class Response
|
|
attr_reader :uri
|
|
|
|
def initialize(uri, body)
|
|
@uri = uri
|
|
@json = Oj.load(body, mode: :strict)
|
|
|
|
validate_response!
|
|
end
|
|
|
|
def subject
|
|
@json['subject']
|
|
end
|
|
|
|
def link(rel, attribute)
|
|
links.dig(rel, attribute)
|
|
end
|
|
|
|
private
|
|
|
|
def links
|
|
@links ||= @json['links'].index_by { |link| link['rel'] }
|
|
end
|
|
|
|
def validate_response!
|
|
raise Webfinger::Error, "Missing subject in response for #{@uri}" if subject.blank?
|
|
end
|
|
end
|
|
|
|
def initialize(uri)
|
|
_, @domain = uri.split('@')
|
|
|
|
raise ArgumentError, 'Webfinger requested for local account' if @domain.nil?
|
|
|
|
@uri = uri
|
|
end
|
|
|
|
def perform
|
|
Response.new(@uri, body_from_webfinger)
|
|
rescue Oj::ParseError
|
|
raise Webfinger::Error, "Invalid JSON in response for #{@uri}"
|
|
rescue Addressable::URI::InvalidURIError
|
|
raise Webfinger::Error, "Invalid URI for #{@uri}"
|
|
end
|
|
|
|
private
|
|
|
|
def body_from_webfinger(url = standard_url, use_fallback = true)
|
|
webfinger_request(url).perform do |res|
|
|
if res.code == 200
|
|
body = res.body_with_limit
|
|
raise Webfinger::Error, "Request for #{@uri} returned empty response" if body.empty?
|
|
body
|
|
elsif res.code == 404 && use_fallback
|
|
body_from_host_meta
|
|
elsif res.code == 410
|
|
raise Webfinger::GoneError, "#{@uri} is gone from the server"
|
|
else
|
|
raise Webfinger::Error, "Request for #{@uri} returned HTTP #{res.code}"
|
|
end
|
|
end
|
|
end
|
|
|
|
def body_from_host_meta
|
|
host_meta_request.perform do |res|
|
|
if res.code == 200
|
|
body_from_webfinger(url_from_template(res.body_with_limit), false)
|
|
else
|
|
raise Webfinger::Error, "Request for #{@uri} returned HTTP #{res.code}"
|
|
end
|
|
end
|
|
end
|
|
|
|
def url_from_template(str)
|
|
link = Nokogiri::XML(str).at_xpath('//xmlns:Link[@rel="lrdd"]')
|
|
|
|
if link.present?
|
|
link['template'].gsub('{uri}', @uri)
|
|
else
|
|
raise Webfinger::Error, "Request for #{@uri} returned host-meta without link to Webfinger"
|
|
end
|
|
rescue Nokogiri::XML::XPath::SyntaxError
|
|
raise Webfinger::Error, "Invalid XML encountered in host-meta for #{@uri}"
|
|
end
|
|
|
|
def host_meta_request
|
|
Request.new(:get, host_meta_url).add_headers('Accept' => 'application/xrd+xml, application/xml, text/xml')
|
|
end
|
|
|
|
def webfinger_request(url)
|
|
Request.new(:get, url).add_headers('Accept' => 'application/jrd+json, application/json')
|
|
end
|
|
|
|
def standard_url
|
|
if @domain.end_with? ".onion"
|
|
"http://#{@domain}/.well-known/webfinger?resource=#{@uri}"
|
|
else
|
|
"https://#{@domain}/.well-known/webfinger?resource=#{@uri}"
|
|
end
|
|
end
|
|
|
|
def host_meta_url
|
|
if @domain.end_with? ".onion"
|
|
"http://#{@domain}/.well-known/host-meta"
|
|
else
|
|
"https://#{@domain}/.well-known/host-meta"
|
|
end
|
|
end
|
|
end
|