You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

164 lines
4.6 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. # frozen_string_literal: true
  2. class ResolveRemoteAccountService < BaseService
  3. include OStatus2::MagicKey
  4. DFRN_NS = 'http://purl.org/macgirvin/dfrn/1.0'
  5. # Find or create a local account for a remote user.
  6. # When creating, look up the user's webfinger and fetch all
  7. # important information from their feed
  8. # @param [String] uri User URI in the form of username@domain
  9. # @return [Account]
  10. def call(uri, update_profile = true, redirected = nil)
  11. @username, @domain = uri.split('@')
  12. return Account.find_local(@username) if TagManager.instance.local_domain?(@domain)
  13. @account = Account.find_remote(@username, @domain)
  14. return @account unless webfinger_update_due?
  15. Rails.logger.debug "Looking up webfinger for #{uri}"
  16. @webfinger = Goldfinger.finger("acct:#{uri}")
  17. confirmed_username, confirmed_domain = @webfinger.subject.gsub(/\Aacct:/, '').split('@')
  18. if confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero?
  19. @username = confirmed_username
  20. @domain = confirmed_domain
  21. elsif redirected.nil?
  22. return call("#{confirmed_username}@#{confirmed_domain}", update_profile, true)
  23. else
  24. Rails.logger.debug 'Requested and returned acct URIs do not match'
  25. return
  26. end
  27. return if links_missing?
  28. return Account.find_local(@username) if TagManager.instance.local_domain?(@domain)
  29. RedisLock.acquire(lock_options) do |lock|
  30. if lock.acquired?
  31. @account = Account.find_remote(@username, @domain)
  32. create_account if @account.nil?
  33. update_account
  34. update_account_profile if update_profile
  35. end
  36. end
  37. @account
  38. rescue Goldfinger::Error => e
  39. Rails.logger.debug "Webfinger query for #{uri} unsuccessful: #{e}"
  40. nil
  41. end
  42. private
  43. def links_missing?
  44. @webfinger.link('http://schemas.google.com/g/2010#updates-from').nil? ||
  45. @webfinger.link('salmon').nil? ||
  46. @webfinger.link('http://webfinger.net/rel/profile-page').nil? ||
  47. @webfinger.link('magic-public-key').nil? ||
  48. canonical_uri.nil? ||
  49. hub_url.nil?
  50. end
  51. def webfinger_update_due?
  52. @account.nil? || @account.last_webfingered_at.nil? || @account.last_webfingered_at <= 1.day.ago
  53. end
  54. def create_account
  55. Rails.logger.debug "Creating new remote account for #{@username}@#{@domain}"
  56. @account = Account.new(username: @username, domain: @domain)
  57. @account.suspended = true if auto_suspend?
  58. @account.silenced = true if auto_silence?
  59. @account.private_key = nil
  60. end
  61. def update_account
  62. @account.last_webfingered_at = Time.now.utc
  63. @account.remote_url = atom_url
  64. @account.salmon_url = salmon_url
  65. @account.url = url
  66. @account.public_key = public_key
  67. @account.uri = canonical_uri
  68. @account.hub_url = hub_url
  69. @account.save!
  70. end
  71. def auto_suspend?
  72. domain_block && domain_block.suspend?
  73. end
  74. def auto_silence?
  75. domain_block && domain_block.silence?
  76. end
  77. def domain_block
  78. return @domain_block if defined?(@domain_block)
  79. @domain_block = DomainBlock.find_by(domain: @domain)
  80. end
  81. def atom_url
  82. @atom_url ||= @webfinger.link('http://schemas.google.com/g/2010#updates-from').href
  83. end
  84. def salmon_url
  85. @salmon_url ||= @webfinger.link('salmon').href
  86. end
  87. def url
  88. @url ||= @webfinger.link('http://webfinger.net/rel/profile-page').href
  89. end
  90. def public_key
  91. @public_key ||= magic_key_to_pem(@webfinger.link('magic-public-key').href)
  92. end
  93. def canonical_uri
  94. return @canonical_uri if defined?(@canonical_uri)
  95. author_uri = atom.at_xpath('/xmlns:feed/xmlns:author/xmlns:uri')
  96. if author_uri.nil?
  97. owner = atom.at_xpath('/xmlns:feed').at_xpath('./dfrn:owner', dfrn: DFRN_NS)
  98. author_uri = owner.at_xpath('./xmlns:uri') unless owner.nil?
  99. end
  100. @canonical_uri = author_uri.nil? ? nil : author_uri.content
  101. end
  102. def hub_url
  103. return @hub_url if defined?(@hub_url)
  104. hubs = atom.xpath('//xmlns:link[@rel="hub"]')
  105. @hub_url = hubs.empty? || hubs.first['href'].nil? ? nil : hubs.first['href']
  106. end
  107. def atom_body
  108. return @atom_body if defined?(@atom_body)
  109. response = Request.new(:get, atom_url).perform
  110. raise Mastodon::UnexpectedResponseError, response unless response.code == 200
  111. @atom_body = response.to_s
  112. end
  113. def atom
  114. return @atom if defined?(@atom)
  115. @atom = Nokogiri::XML(atom_body)
  116. end
  117. def update_account_profile
  118. RemoteProfileUpdateWorker.perform_async(@account.id, atom_body.force_encoding('UTF-8'), false)
  119. end
  120. def lock_options
  121. { redis: Redis.current, key: "resolve:#{@username}@#{@domain}" }
  122. end
  123. end