commit: 613e7c7521252bd85e473d9c63cbc8b8e1a733a8
parent: 17cecd75cadfd9914ffc233de01d41204ef7802c
Author: Akihiko Odaki <akihiko.odaki.4i@stu.hosei.ac.jp>
Date: Mon, 22 Jan 2018 22:25:09 +0900
Rename ResolveRemoteAccountService to ResolveAccountService (#6327)
The service used to be named ResolveRemoteAccountService resolves local
accounts as well.
Diffstat:
20 files changed, 374 insertions(+), 374 deletions(-)
diff --git a/app/controllers/activitypub/inboxes_controller.rb b/app/controllers/activitypub/inboxes_controller.rb
@@ -28,7 +28,7 @@ class ActivityPub::InboxesController < Api::BaseController
def upgrade_account
if signed_request_account.ostatus?
signed_request_account.update(last_webfingered_at: nil)
- ResolveRemoteAccountWorker.perform_async(signed_request_account.acct)
+ ResolveAccountWorker.perform_async(signed_request_account.acct)
end
Pubsubhubbub::UnsubscribeWorker.perform_async(signed_request_account.id) if signed_request_account.subscribed?
diff --git a/app/controllers/authorize_follows_controller.rb b/app/controllers/authorize_follows_controller.rb
@@ -41,7 +41,7 @@ class AuthorizeFollowsController < ApplicationController
end
def account_from_remote_follow
- ResolveRemoteAccountService.new.call(acct_without_prefix)
+ ResolveAccountService.new.call(acct_without_prefix)
end
def acct_param_is_url?
diff --git a/app/controllers/concerns/signature_verification.rb b/app/controllers/concerns/signature_verification.rb
@@ -114,7 +114,7 @@ module SignatureVerification
def account_from_key_id(key_id)
if key_id.start_with?('acct:')
- ResolveRemoteAccountService.new.call(key_id.gsub(/\Aacct:/, ''))
+ ResolveAccountService.new.call(key_id.gsub(/\Aacct:/, ''))
elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
account = ActivityPub::TagManager.instance.uri_to_resource(key_id, Account)
account ||= ActivityPub::FetchRemoteKeyService.new.call(key_id, id: false)
diff --git a/app/models/account.rb b/app/models/account.rb
@@ -163,7 +163,7 @@ class Account < ApplicationRecord
def refresh!
return if local?
- ResolveRemoteAccountService.new.call(acct)
+ ResolveAccountService.new.call(acct)
end
def unsuspend!
diff --git a/app/models/form/migration.rb b/app/models/form/migration.rb
@@ -20,6 +20,6 @@ class Form::Migration
private
def set_account
- self.account = (ResolveRemoteAccountService.new.call(acct) if account.nil? && acct.present?)
+ self.account = (ResolveAccountService.new.call(acct) if account.nil? && acct.present?)
end
end
diff --git a/app/services/account_search_service.rb b/app/services/account_search_service.rb
@@ -18,7 +18,7 @@ class AccountSearchService < BaseService
return [] if query_blank_or_hashtag? || limit < 1
if resolving_non_matching_remote_account?
- [ResolveRemoteAccountService.new.call("#{query_username}@#{query_domain}")].compact
+ [ResolveAccountService.new.call("#{query_username}@#{query_domain}")].compact
else
search_results_and_exact_match.compact.uniq.slice(0, limit)
end
diff --git a/app/services/concerns/author_extractor.rb b/app/services/concerns/author_extractor.rb
@@ -18,6 +18,6 @@ module AuthorExtractor
acct = "#{username}@#{domain}"
end
- ResolveRemoteAccountService.new.call(acct, update_profile)
+ ResolveAccountService.new.call(acct, update_profile)
end
end
diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb
@@ -9,7 +9,7 @@ class FollowService < BaseService
# @param [true, false, nil] reblogs Whether or not to show reblogs, defaults to true
def call(source_account, uri, reblogs: nil)
reblogs = true if reblogs.nil?
- target_account = uri.is_a?(Account) ? uri : ResolveRemoteAccountService.new.call(uri)
+ target_account = uri.is_a?(Account) ? uri : ResolveAccountService.new.call(uri)
raise ActiveRecord::RecordNotFound if target_account.nil? || target_account.id == source_account.id || target_account.suspended?
raise Mastodon::NotPermittedError if target_account.blocking?(source_account) || source_account.blocking?(target_account)
diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb
@@ -16,7 +16,7 @@ class ProcessMentionsService < BaseService
if mention_undeliverable?(status, mentioned_account)
begin
- mentioned_account = resolve_remote_account_service.call($1)
+ mentioned_account = resolve_account_service.call($1)
rescue Goldfinger::Error, HTTP::Error
mentioned_account = nil
end
@@ -63,7 +63,7 @@ class ProcessMentionsService < BaseService
).as_json).sign!(status.account))
end
- def resolve_remote_account_service
- ResolveRemoteAccountService.new
+ def resolve_account_service
+ ResolveAccountService.new
end
end
diff --git a/app/services/resolve_account_service.rb b/app/services/resolve_account_service.rb
@@ -0,0 +1,208 @@
+# frozen_string_literal: true
+
+class ResolveAccountService < BaseService
+ include OStatus2::MagicKey
+ include JsonLdHelper
+
+ DFRN_NS = 'http://purl.org/macgirvin/dfrn/1.0'
+
+ # Find or create a local account for a remote user.
+ # When creating, look up the user's webfinger and fetch all
+ # important information from their feed
+ # @param [String] uri User URI in the form of username@domain
+ # @return [Account]
+ def call(uri, update_profile = true, redirected = nil)
+ @username, @domain = uri.split('@')
+ @update_profile = update_profile
+
+ return Account.find_local(@username) if TagManager.instance.local_domain?(@domain)
+
+ @account = Account.find_remote(@username, @domain)
+
+ return @account unless webfinger_update_due?
+
+ Rails.logger.debug "Looking up webfinger for #{uri}"
+
+ @webfinger = Goldfinger.finger("acct:#{uri}")
+
+ confirmed_username, confirmed_domain = @webfinger.subject.gsub(/\Aacct:/, '').split('@')
+
+ if confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero?
+ @username = confirmed_username
+ @domain = confirmed_domain
+ elsif redirected.nil?
+ return call("#{confirmed_username}@#{confirmed_domain}", update_profile, true)
+ else
+ Rails.logger.debug 'Requested and returned acct URIs do not match'
+ return
+ end
+
+ return if links_missing?
+ return Account.find_local(@username) if TagManager.instance.local_domain?(@domain)
+
+ RedisLock.acquire(lock_options) do |lock|
+ if lock.acquired?
+ @account = Account.find_remote(@username, @domain)
+
+ if activitypub_ready? || @account&.activitypub?
+ handle_activitypub
+ else
+ handle_ostatus
+ end
+ end
+ end
+
+ @account
+ rescue Goldfinger::Error => e
+ Rails.logger.debug "Webfinger query for #{uri} unsuccessful: #{e}"
+ nil
+ end
+
+ private
+
+ def links_missing?
+ !(activitypub_ready? || ostatus_ready?)
+ end
+
+ def ostatus_ready?
+ !(@webfinger.link('http://schemas.google.com/g/2010#updates-from').nil? ||
+ @webfinger.link('salmon').nil? ||
+ @webfinger.link('http://webfinger.net/rel/profile-page').nil? ||
+ @webfinger.link('magic-public-key').nil? ||
+ canonical_uri.nil? ||
+ hub_url.nil?)
+ end
+
+ def webfinger_update_due?
+ @account.nil? || @account.possibly_stale?
+ end
+
+ def activitypub_ready?
+ !@webfinger.link('self').nil? &&
+ ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(@webfinger.link('self').type) &&
+ !actor_json.nil? &&
+ actor_json['inbox'].present?
+ end
+
+ def handle_ostatus
+ create_account if @account.nil?
+ update_account
+ update_account_profile if update_profile?
+ end
+
+ def update_profile?
+ @update_profile
+ end
+
+ def handle_activitypub
+ return if actor_json.nil?
+
+ @account = ActivityPub::ProcessAccountService.new.call(@username, @domain, actor_json)
+ rescue Oj::ParseError
+ nil
+ end
+
+ def create_account
+ Rails.logger.debug "Creating new remote account for #{@username}@#{@domain}"
+
+ @account = Account.new(username: @username, domain: @domain)
+ @account.suspended = true if auto_suspend?
+ @account.silenced = true if auto_silence?
+ @account.private_key = nil
+ end
+
+ def update_account
+ @account.last_webfingered_at = Time.now.utc
+ @account.protocol = :ostatus
+ @account.remote_url = atom_url
+ @account.salmon_url = salmon_url
+ @account.url = url
+ @account.public_key = public_key
+ @account.uri = canonical_uri
+ @account.hub_url = hub_url
+ @account.save!
+ end
+
+ def auto_suspend?
+ domain_block&.suspend?
+ end
+
+ def auto_silence?
+ domain_block&.silence?
+ end
+
+ def domain_block
+ return @domain_block if defined?(@domain_block)
+ @domain_block = DomainBlock.find_by(domain: @domain)
+ end
+
+ def atom_url
+ @atom_url ||= @webfinger.link('http://schemas.google.com/g/2010#updates-from').href
+ end
+
+ def salmon_url
+ @salmon_url ||= @webfinger.link('salmon').href
+ end
+
+ def actor_url
+ @actor_url ||= @webfinger.link('self').href
+ end
+
+ def url
+ @url ||= @webfinger.link('http://webfinger.net/rel/profile-page').href
+ end
+
+ def public_key
+ @public_key ||= magic_key_to_pem(@webfinger.link('magic-public-key').href)
+ end
+
+ def canonical_uri
+ return @canonical_uri if defined?(@canonical_uri)
+
+ author_uri = atom.at_xpath('/xmlns:feed/xmlns:author/xmlns:uri')
+
+ if author_uri.nil?
+ owner = atom.at_xpath('/xmlns:feed').at_xpath('./dfrn:owner', dfrn: DFRN_NS)
+ author_uri = owner.at_xpath('./xmlns:uri') unless owner.nil?
+ end
+
+ @canonical_uri = author_uri.nil? ? nil : author_uri.content
+ end
+
+ def hub_url
+ return @hub_url if defined?(@hub_url)
+
+ hubs = atom.xpath('//xmlns:link[@rel="hub"]')
+ @hub_url = hubs.empty? || hubs.first['href'].nil? ? nil : hubs.first['href']
+ end
+
+ def atom_body
+ return @atom_body if defined?(@atom_body)
+
+ response = Request.new(:get, atom_url).perform
+
+ raise Mastodon::UnexpectedResponseError, response unless response.code == 200
+
+ @atom_body = response.to_s
+ end
+
+ def actor_json
+ return @actor_json if defined?(@actor_json)
+
+ json = fetch_resource(actor_url, false)
+ @actor_json = supported_context?(json) && json['type'] == 'Person' ? json : nil
+ end
+
+ def atom
+ return @atom if defined?(@atom)
+ @atom = Nokogiri::XML(atom_body)
+ end
+
+ def update_account_profile
+ RemoteProfileUpdateWorker.perform_async(@account.id, atom_body.force_encoding('UTF-8'), false)
+ end
+
+ def lock_options
+ { redis: Redis.current, key: "resolve:#{@username}@#{@domain}" }
+ end
+end
diff --git a/app/services/resolve_remote_account_service.rb b/app/services/resolve_remote_account_service.rb
@@ -1,208 +0,0 @@
-# frozen_string_literal: true
-
-class ResolveRemoteAccountService < BaseService
- include OStatus2::MagicKey
- include JsonLdHelper
-
- DFRN_NS = 'http://purl.org/macgirvin/dfrn/1.0'
-
- # Find or create a local account for a remote user.
- # When creating, look up the user's webfinger and fetch all
- # important information from their feed
- # @param [String] uri User URI in the form of username@domain
- # @return [Account]
- def call(uri, update_profile = true, redirected = nil)
- @username, @domain = uri.split('@')
- @update_profile = update_profile
-
- return Account.find_local(@username) if TagManager.instance.local_domain?(@domain)
-
- @account = Account.find_remote(@username, @domain)
-
- return @account unless webfinger_update_due?
-
- Rails.logger.debug "Looking up webfinger for #{uri}"
-
- @webfinger = Goldfinger.finger("acct:#{uri}")
-
- confirmed_username, confirmed_domain = @webfinger.subject.gsub(/\Aacct:/, '').split('@')
-
- if confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero?
- @username = confirmed_username
- @domain = confirmed_domain
- elsif redirected.nil?
- return call("#{confirmed_username}@#{confirmed_domain}", update_profile, true)
- else
- Rails.logger.debug 'Requested and returned acct URIs do not match'
- return
- end
-
- return if links_missing?
- return Account.find_local(@username) if TagManager.instance.local_domain?(@domain)
-
- RedisLock.acquire(lock_options) do |lock|
- if lock.acquired?
- @account = Account.find_remote(@username, @domain)
-
- if activitypub_ready? || @account&.activitypub?
- handle_activitypub
- else
- handle_ostatus
- end
- end
- end
-
- @account
- rescue Goldfinger::Error => e
- Rails.logger.debug "Webfinger query for #{uri} unsuccessful: #{e}"
- nil
- end
-
- private
-
- def links_missing?
- !(activitypub_ready? || ostatus_ready?)
- end
-
- def ostatus_ready?
- !(@webfinger.link('http://schemas.google.com/g/2010#updates-from').nil? ||
- @webfinger.link('salmon').nil? ||
- @webfinger.link('http://webfinger.net/rel/profile-page').nil? ||
- @webfinger.link('magic-public-key').nil? ||
- canonical_uri.nil? ||
- hub_url.nil?)
- end
-
- def webfinger_update_due?
- @account.nil? || @account.possibly_stale?
- end
-
- def activitypub_ready?
- !@webfinger.link('self').nil? &&
- ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(@webfinger.link('self').type) &&
- !actor_json.nil? &&
- actor_json['inbox'].present?
- end
-
- def handle_ostatus
- create_account if @account.nil?
- update_account
- update_account_profile if update_profile?
- end
-
- def update_profile?
- @update_profile
- end
-
- def handle_activitypub
- return if actor_json.nil?
-
- @account = ActivityPub::ProcessAccountService.new.call(@username, @domain, actor_json)
- rescue Oj::ParseError
- nil
- end
-
- def create_account
- Rails.logger.debug "Creating new remote account for #{@username}@#{@domain}"
-
- @account = Account.new(username: @username, domain: @domain)
- @account.suspended = true if auto_suspend?
- @account.silenced = true if auto_silence?
- @account.private_key = nil
- end
-
- def update_account
- @account.last_webfingered_at = Time.now.utc
- @account.protocol = :ostatus
- @account.remote_url = atom_url
- @account.salmon_url = salmon_url
- @account.url = url
- @account.public_key = public_key
- @account.uri = canonical_uri
- @account.hub_url = hub_url
- @account.save!
- end
-
- def auto_suspend?
- domain_block&.suspend?
- end
-
- def auto_silence?
- domain_block&.silence?
- end
-
- def domain_block
- return @domain_block if defined?(@domain_block)
- @domain_block = DomainBlock.find_by(domain: @domain)
- end
-
- def atom_url
- @atom_url ||= @webfinger.link('http://schemas.google.com/g/2010#updates-from').href
- end
-
- def salmon_url
- @salmon_url ||= @webfinger.link('salmon').href
- end
-
- def actor_url
- @actor_url ||= @webfinger.link('self').href
- end
-
- def url
- @url ||= @webfinger.link('http://webfinger.net/rel/profile-page').href
- end
-
- def public_key
- @public_key ||= magic_key_to_pem(@webfinger.link('magic-public-key').href)
- end
-
- def canonical_uri
- return @canonical_uri if defined?(@canonical_uri)
-
- author_uri = atom.at_xpath('/xmlns:feed/xmlns:author/xmlns:uri')
-
- if author_uri.nil?
- owner = atom.at_xpath('/xmlns:feed').at_xpath('./dfrn:owner', dfrn: DFRN_NS)
- author_uri = owner.at_xpath('./xmlns:uri') unless owner.nil?
- end
-
- @canonical_uri = author_uri.nil? ? nil : author_uri.content
- end
-
- def hub_url
- return @hub_url if defined?(@hub_url)
-
- hubs = atom.xpath('//xmlns:link[@rel="hub"]')
- @hub_url = hubs.empty? || hubs.first['href'].nil? ? nil : hubs.first['href']
- end
-
- def atom_body
- return @atom_body if defined?(@atom_body)
-
- response = Request.new(:get, atom_url).perform
-
- raise Mastodon::UnexpectedResponseError, response unless response.code == 200
-
- @atom_body = response.to_s
- end
-
- def actor_json
- return @actor_json if defined?(@actor_json)
-
- json = fetch_resource(actor_url, false)
- @actor_json = supported_context?(json) && json['type'] == 'Person' ? json : nil
- end
-
- def atom
- return @atom if defined?(@atom)
- @atom = Nokogiri::XML(atom_body)
- end
-
- def update_account_profile
- RemoteProfileUpdateWorker.perform_async(@account.id, atom_body.force_encoding('UTF-8'), false)
- end
-
- def lock_options
- { redis: Redis.current, key: "resolve:#{@username}@#{@domain}" }
- end
-end
diff --git a/app/workers/import/relationship_worker.rb b/app/workers/import/relationship_worker.rb
@@ -7,7 +7,7 @@ class Import::RelationshipWorker
def perform(account_id, target_account_uri, relationship)
from_account = Account.find(account_id)
- target_account = ResolveRemoteAccountService.new.call(target_account_uri)
+ target_account = ResolveAccountService.new.call(target_account_uri)
return if target_account.nil?
diff --git a/app/workers/resolve_account_worker.rb b/app/workers/resolve_account_worker.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class ResolveAccountWorker
+ include Sidekiq::Worker
+
+ sidekiq_options queue: 'pull', unique: :until_executed
+
+ def perform(uri)
+ ResolveAccountService.new.call(uri)
+ end
+end
diff --git a/app/workers/resolve_remote_account_worker.rb b/app/workers/resolve_remote_account_worker.rb
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-class ResolveRemoteAccountWorker
- include Sidekiq::Worker
-
- sidekiq_options queue: 'pull', unique: :until_executed
-
- def perform(uri)
- ResolveRemoteAccountService.new.call(uri)
- end
-end
diff --git a/spec/controllers/authorize_follows_controller_spec.rb b/spec/controllers/authorize_follows_controller_spec.rb
@@ -30,7 +30,7 @@ describe AuthorizeFollowsController do
it 'renders error when account cant be found' do
service = double
- allow(ResolveRemoteAccountService).to receive(:new).and_return(service)
+ allow(ResolveAccountService).to receive(:new).and_return(service)
allow(service).to receive(:call).with('missing@hostname').and_return(nil)
get :show, params: { acct: 'acct:missing@hostname' }
@@ -54,7 +54,7 @@ describe AuthorizeFollowsController do
it 'sets account from acct uri' do
account = Account.new
service = double
- allow(ResolveRemoteAccountService).to receive(:new).and_return(service)
+ allow(ResolveAccountService).to receive(:new).and_return(service)
allow(service).to receive(:call).with('found@hostname').and_return(account)
get :show, params: { acct: 'acct:found@hostname' }
diff --git a/spec/controllers/settings/imports_controller_spec.rb b/spec/controllers/settings/imports_controller_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Settings::ImportsController, type: :controller do
describe 'POST #create' do
it 'redirects to settings path with successful following import' do
service = double(call: nil)
- allow(ResolveRemoteAccountService).to receive(:new).and_return(service)
+ allow(ResolveAccountService).to receive(:new).and_return(service)
post :create, params: {
import: {
type: 'following',
@@ -30,7 +30,7 @@ RSpec.describe Settings::ImportsController, type: :controller do
it 'redirects to settings path with successful blocking import' do
service = double(call: nil)
- allow(ResolveRemoteAccountService).to receive(:new).and_return(service)
+ allow(ResolveAccountService).to receive(:new).and_return(service)
post :create, params: {
import: {
type: 'blocking',
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
@@ -185,8 +185,8 @@ RSpec.describe Account, type: :model do
expect(account.refresh!).to be_nil
end
- it 'calls not ResolveRemoteAccountService#call' do
- expect_any_instance_of(ResolveRemoteAccountService).not_to receive(:call).with(acct)
+ it 'calls not ResolveAccountService#call' do
+ expect_any_instance_of(ResolveAccountService).not_to receive(:call).with(acct)
account.refresh!
end
end
@@ -194,8 +194,8 @@ RSpec.describe Account, type: :model do
context 'domain is present' do
let(:domain) { 'example.com' }
- it 'calls ResolveRemoteAccountService#call' do
- expect_any_instance_of(ResolveRemoteAccountService).to receive(:call).with(acct).once
+ it 'calls ResolveAccountService#call' do
+ expect_any_instance_of(ResolveAccountService).to receive(:call).with(acct).once
account.refresh!
end
end
diff --git a/spec/services/account_search_service_spec.rb b/spec/services/account_search_service_spec.rb
@@ -123,7 +123,7 @@ describe AccountSearchService do
describe 'when there is a domain but no exact match' do
it 'follows the remote account when resolve is true' do
service = double(call: nil)
- allow(ResolveRemoteAccountService).to receive(:new).and_return(service)
+ allow(ResolveAccountService).to receive(:new).and_return(service)
results = subject.call('newuser@remote.com', 10, nil, resolve: true)
expect(service).to have_received(:call).with('newuser@remote.com')
@@ -131,7 +131,7 @@ describe AccountSearchService do
it 'does not follow the remote account when resolve is false' do
service = double(call: nil)
- allow(ResolveRemoteAccountService).to receive(:new).and_return(service)
+ allow(ResolveAccountService).to receive(:new).and_return(service)
results = subject.call('newuser@remote.com', 10, nil, resolve: false)
expect(service).not_to have_received(:call)
diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb
@@ -0,0 +1,133 @@
+require 'rails_helper'
+
+RSpec.describe ResolveAccountService do
+ subject { described_class.new }
+
+ before do
+ stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
+ stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:catsrgr8@example.com").to_return(status: 404)
+ stub_request(:get, "https://redirected.com/.well-known/host-meta").to_return(request_fixture('redirected.host-meta.txt'))
+ stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
+ stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no").to_return(request_fixture('webfinger.txt'))
+ stub_request(:get, "https://redirected.com/.well-known/webfinger?resource=acct:gargron@redirected.com").to_return(request_fixture('webfinger.txt'))
+ stub_request(:get, "https://redirected.com/.well-known/webfinger?resource=acct:hacker1@redirected.com").to_return(request_fixture('webfinger-hacker1.txt'))
+ stub_request(:get, "https://redirected.com/.well-known/webfinger?resource=acct:hacker2@redirected.com").to_return(request_fixture('webfinger-hacker2.txt'))
+ stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:catsrgr8@quitter.no").to_return(status: 404)
+ stub_request(:get, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
+ stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
+ stub_request(:get, "https://localdomain.com/.well-known/host-meta").to_return(request_fixture('localdomain-hostmeta.txt'))
+ stub_request(:get, "https://localdomain.com/.well-known/webfinger?resource=acct:foo@localdomain.com").to_return(status: 404)
+ stub_request(:get, "https://webdomain.com/.well-known/webfinger?resource=acct:foo@localdomain.com").to_return(request_fixture('localdomain-webfinger.txt'))
+ stub_request(:get, "https://webdomain.com/users/foo.atom").to_return(request_fixture('localdomain-feed.txt'))
+ end
+
+ it 'raises error if no such user can be resolved via webfinger' do
+ expect(subject.call('catsrgr8@quitter.no')).to be_nil
+ end
+
+ it 'raises error if the domain does not have webfinger' do
+ expect(subject.call('catsrgr8@example.com')).to be_nil
+ end
+
+ it 'prevents hijacking existing accounts' do
+ account = subject.call('hacker1@redirected.com')
+ expect(account.salmon_url).to_not eq 'https://hacker.com/main/salmon/user/7477'
+ end
+
+ it 'prevents hijacking inexisting accounts' do
+ expect(subject.call('hacker2@redirected.com')).to be_nil
+ end
+
+ context 'with an OStatus account' do
+ it 'returns an already existing remote account' do
+ old_account = Fabricate(:account, username: 'gargron', domain: 'quitter.no')
+ returned_account = subject.call('gargron@quitter.no')
+
+ expect(old_account.id).to eq returned_account.id
+ end
+
+ it 'returns a new remote account' do
+ account = subject.call('gargron@quitter.no')
+
+ expect(account.username).to eq 'gargron'
+ expect(account.domain).to eq 'quitter.no'
+ expect(account.remote_url).to eq 'https://quitter.no/api/statuses/user_timeline/7477.atom'
+ end
+
+ it 'follows a legitimate account redirection' do
+ account = subject.call('gargron@redirected.com')
+
+ expect(account.username).to eq 'gargron'
+ expect(account.domain).to eq 'quitter.no'
+ expect(account.remote_url).to eq 'https://quitter.no/api/statuses/user_timeline/7477.atom'
+ end
+
+ it 'returns a new remote account' do
+ account = subject.call('foo@localdomain.com')
+
+ expect(account.username).to eq 'foo'
+ expect(account.domain).to eq 'localdomain.com'
+ expect(account.remote_url).to eq 'https://webdomain.com/users/foo.atom'
+ end
+ end
+
+ context 'with an ActivityPub account' do
+ before do
+ stub_request(:get, "https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com").to_return(request_fixture('activitypub-webfinger.txt'))
+ stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor.txt'))
+ stub_request(:get, "https://ap.example.com/users/foo.atom").to_return(request_fixture('activitypub-feed.txt'))
+ stub_request(:get, %r{https://ap.example.com/users/foo/\w+}).to_return(status: 404)
+ end
+
+ it 'fallback to OStatus if actor json could not be fetched' do
+ stub_request(:get, "https://ap.example.com/users/foo").to_return(status: 404)
+
+ account = subject.call('foo@ap.example.com')
+
+ expect(account.ostatus?).to eq true
+ expect(account.remote_url).to eq 'https://ap.example.com/users/foo.atom'
+ end
+
+ it 'fallback to OStatus if actor json did not have inbox_url' do
+ stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor-noinbox.txt'))
+
+ account = subject.call('foo@ap.example.com')
+
+ expect(account.ostatus?).to eq true
+ expect(account.remote_url).to eq 'https://ap.example.com/users/foo.atom'
+ end
+
+ it 'returns new remote account' do
+ account = subject.call('foo@ap.example.com')
+
+ expect(account.activitypub?).to eq true
+ expect(account.domain).to eq 'ap.example.com'
+ expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
+ end
+
+ pending
+ end
+
+ it 'processes one remote account at a time using locks' do
+ wait_for_start = true
+ fail_occurred = false
+ return_values = []
+
+ threads = Array.new(5) do
+ Thread.new do
+ true while wait_for_start
+ begin
+ return_values << described_class.new.call('foo@localdomain.com')
+ rescue ActiveRecord::RecordNotUnique
+ fail_occurred = true
+ end
+ end
+ end
+
+ wait_for_start = false
+ threads.each(&:join)
+
+ expect(fail_occurred).to be false
+ expect(return_values).to_not include(nil)
+ end
+end
diff --git a/spec/services/resolve_remote_account_service_spec.rb b/spec/services/resolve_remote_account_service_spec.rb
@@ -1,133 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe ResolveRemoteAccountService do
- subject { described_class.new }
-
- before do
- stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
- stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:catsrgr8@example.com").to_return(status: 404)
- stub_request(:get, "https://redirected.com/.well-known/host-meta").to_return(request_fixture('redirected.host-meta.txt'))
- stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
- stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no").to_return(request_fixture('webfinger.txt'))
- stub_request(:get, "https://redirected.com/.well-known/webfinger?resource=acct:gargron@redirected.com").to_return(request_fixture('webfinger.txt'))
- stub_request(:get, "https://redirected.com/.well-known/webfinger?resource=acct:hacker1@redirected.com").to_return(request_fixture('webfinger-hacker1.txt'))
- stub_request(:get, "https://redirected.com/.well-known/webfinger?resource=acct:hacker2@redirected.com").to_return(request_fixture('webfinger-hacker2.txt'))
- stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:catsrgr8@quitter.no").to_return(status: 404)
- stub_request(:get, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
- stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
- stub_request(:get, "https://localdomain.com/.well-known/host-meta").to_return(request_fixture('localdomain-hostmeta.txt'))
- stub_request(:get, "https://localdomain.com/.well-known/webfinger?resource=acct:foo@localdomain.com").to_return(status: 404)
- stub_request(:get, "https://webdomain.com/.well-known/webfinger?resource=acct:foo@localdomain.com").to_return(request_fixture('localdomain-webfinger.txt'))
- stub_request(:get, "https://webdomain.com/users/foo.atom").to_return(request_fixture('localdomain-feed.txt'))
- end
-
- it 'raises error if no such user can be resolved via webfinger' do
- expect(subject.call('catsrgr8@quitter.no')).to be_nil
- end
-
- it 'raises error if the domain does not have webfinger' do
- expect(subject.call('catsrgr8@example.com')).to be_nil
- end
-
- it 'prevents hijacking existing accounts' do
- account = subject.call('hacker1@redirected.com')
- expect(account.salmon_url).to_not eq 'https://hacker.com/main/salmon/user/7477'
- end
-
- it 'prevents hijacking inexisting accounts' do
- expect(subject.call('hacker2@redirected.com')).to be_nil
- end
-
- context 'with an OStatus account' do
- it 'returns an already existing remote account' do
- old_account = Fabricate(:account, username: 'gargron', domain: 'quitter.no')
- returned_account = subject.call('gargron@quitter.no')
-
- expect(old_account.id).to eq returned_account.id
- end
-
- it 'returns a new remote account' do
- account = subject.call('gargron@quitter.no')
-
- expect(account.username).to eq 'gargron'
- expect(account.domain).to eq 'quitter.no'
- expect(account.remote_url).to eq 'https://quitter.no/api/statuses/user_timeline/7477.atom'
- end
-
- it 'follows a legitimate account redirection' do
- account = subject.call('gargron@redirected.com')
-
- expect(account.username).to eq 'gargron'
- expect(account.domain).to eq 'quitter.no'
- expect(account.remote_url).to eq 'https://quitter.no/api/statuses/user_timeline/7477.atom'
- end
-
- it 'returns a new remote account' do
- account = subject.call('foo@localdomain.com')
-
- expect(account.username).to eq 'foo'
- expect(account.domain).to eq 'localdomain.com'
- expect(account.remote_url).to eq 'https://webdomain.com/users/foo.atom'
- end
- end
-
- context 'with an ActivityPub account' do
- before do
- stub_request(:get, "https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com").to_return(request_fixture('activitypub-webfinger.txt'))
- stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor.txt'))
- stub_request(:get, "https://ap.example.com/users/foo.atom").to_return(request_fixture('activitypub-feed.txt'))
- stub_request(:get, %r{https://ap.example.com/users/foo/\w+}).to_return(status: 404)
- end
-
- it 'fallback to OStatus if actor json could not be fetched' do
- stub_request(:get, "https://ap.example.com/users/foo").to_return(status: 404)
-
- account = subject.call('foo@ap.example.com')
-
- expect(account.ostatus?).to eq true
- expect(account.remote_url).to eq 'https://ap.example.com/users/foo.atom'
- end
-
- it 'fallback to OStatus if actor json did not have inbox_url' do
- stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor-noinbox.txt'))
-
- account = subject.call('foo@ap.example.com')
-
- expect(account.ostatus?).to eq true
- expect(account.remote_url).to eq 'https://ap.example.com/users/foo.atom'
- end
-
- it 'returns new remote account' do
- account = subject.call('foo@ap.example.com')
-
- expect(account.activitypub?).to eq true
- expect(account.domain).to eq 'ap.example.com'
- expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
- end
-
- pending
- end
-
- it 'processes one remote account at a time using locks' do
- wait_for_start = true
- fail_occurred = false
- return_values = []
-
- threads = Array.new(5) do
- Thread.new do
- true while wait_for_start
- begin
- return_values << described_class.new.call('foo@localdomain.com')
- rescue ActiveRecord::RecordNotUnique
- fail_occurred = true
- end
- end
- end
-
- wait_for_start = false
- threads.each(&:join)
-
- expect(fail_occurred).to be false
- expect(return_values).to_not include(nil)
- end
-end