commit: f3be6052867343ab15cc84bc91edcaf0c1d70115
parent: aebebdc5d1967e2110a9caafc3238c8b8ec055c4
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 19 Jun 2017 01:51:04 +0200
Rename FollowRemoteAccountService to ResolveRemoteAccountService (#3847)
Rename Activitypub to ActivityPub
Diffstat:
22 files changed, 201 insertions(+), 200 deletions(-)
diff --git a/app/controllers/api/activitypub/activities_controller.rb b/app/controllers/api/activitypub/activities_controller.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class Api::Activitypub::ActivitiesController < Api::BaseController
+class Api::ActivityPub::ActivitiesController < Api::BaseController
include Authorization
# before_action :set_follow, only: [:show_follow]
diff --git a/app/controllers/api/activitypub/notes_controller.rb b/app/controllers/api/activitypub/notes_controller.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class Api::Activitypub::NotesController < Api::BaseController
+class Api::ActivityPub::NotesController < Api::BaseController
include Authorization
before_action :set_status
diff --git a/app/controllers/api/activitypub/outbox_controller.rb b/app/controllers/api/activitypub/outbox_controller.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class Api::Activitypub::OutboxController < Api::BaseController
+class Api::ActivityPub::OutboxController < Api::BaseController
before_action :set_account
respond_to :activitystreams2
diff --git a/app/controllers/authorize_follows_controller.rb b/app/controllers/authorize_follows_controller.rb
@@ -40,7 +40,7 @@ class AuthorizeFollowsController < ApplicationController
end
def account_from_remote_follow
- FollowRemoteAccountService.new.call(acct_without_prefix)
+ ResolveRemoteAccountService.new.call(acct_without_prefix)
end
def acct_param_is_url?
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?
- [FollowRemoteAccountService.new.call("#{query_username}@#{query_domain}")]
+ [ResolveRemoteAccountService.new.call("#{query_username}@#{query_domain}")]
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
- FollowRemoteAccountService.new.call(acct, update_profile)
+ ResolveRemoteAccountService.new.call(acct, update_profile)
end
end
diff --git a/app/services/follow_remote_account_service.rb b/app/services/follow_remote_account_service.rb
@@ -1,108 +0,0 @@
-# frozen_string_literal: true
-
-class FollowRemoteAccountService < BaseService
- include OStatus2::MagicKey
- include HttpHelper
-
- 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('@')
-
- return Account.find_local(username) if TagManager.instance.local_domain?(domain)
-
- account = Account.find_remote(username, domain)
- return account unless account_needs_webfinger_update?(account)
-
- Rails.logger.debug "Looking up webfinger for #{uri}"
-
- data = Goldfinger.finger("acct:#{uri}")
-
- raise Goldfinger::Error, 'Missing resource links' if data.link('http://schemas.google.com/g/2010#updates-from').nil? || data.link('salmon').nil? || data.link('http://webfinger.net/rel/profile-page').nil? || data.link('magic-public-key').nil?
-
- # Disallow account hijacking
- confirmed_username, confirmed_domain = data.subject.gsub(/\Aacct:/, '').split('@')
-
- unless confirmed_username.casecmp(username).zero? && confirmed_domain.casecmp(domain).zero?
- return call("#{confirmed_username}@#{confirmed_domain}", update_profile, true) if redirected.nil?
- raise Goldfinger::Error, 'Requested and returned acct URI do not match'
- end
-
- return Account.find_local(confirmed_username) if TagManager.instance.local_domain?(confirmed_domain)
-
- confirmed_account = Account.find_remote(confirmed_username, confirmed_domain)
- if confirmed_account.nil?
- Rails.logger.debug "Creating new remote account for #{uri}"
-
- domain_block = DomainBlock.find_by(domain: domain)
- account = Account.new(username: confirmed_username, domain: confirmed_domain)
- account.suspended = true if domain_block && domain_block.suspend?
- account.silenced = true if domain_block && domain_block.silence?
- account.private_key = nil
- else
- account = confirmed_account
- end
-
- account.last_webfingered_at = Time.now.utc
-
- account.remote_url = data.link('http://schemas.google.com/g/2010#updates-from').href
- account.salmon_url = data.link('salmon').href
- account.url = data.link('http://webfinger.net/rel/profile-page').href
- account.public_key = magic_key_to_pem(data.link('magic-public-key').href)
-
- body, xml = get_feed(account.remote_url)
- hubs = get_hubs(xml)
-
- account.uri = get_account_uri(xml)
- account.hub_url = hubs.first.attribute('href').value
-
- begin
- account.save!
- get_profile(body, account) if update_profile
- rescue ActiveRecord::RecordNotUnique
- # The account has been added by another worker!
- return Account.find_remote(confirmed_username, confirmed_domain)
- end
-
- account
- end
-
- private
-
- def account_needs_webfinger_update?(account)
- account&.last_webfingered_at.nil? || account.last_webfingered_at <= 1.day.ago
- end
-
- def get_feed(url)
- response = http_client(write: 20, connect: 20, read: 50).get(Addressable::URI.parse(url).normalize)
- raise Goldfinger::Error, "Feed attempt failed for #{url}: HTTP #{response.code}" unless response.code == 200
- [response.to_s, Nokogiri::XML(response)]
- end
-
- def get_hubs(xml)
- hubs = xml.xpath('//xmlns:link[@rel="hub"]')
- raise Goldfinger::Error, 'No PubSubHubbub hubs found' if hubs.empty? || hubs.first.attribute('href').nil?
- hubs
- end
-
- def get_account_uri(xml)
- author_uri = xml.at_xpath('/xmlns:feed/xmlns:author/xmlns:uri')
-
- if author_uri.nil?
- owner = xml.at_xpath('/xmlns:feed').at_xpath('./dfrn:owner', dfrn: DFRN_NS)
- author_uri = owner.at_xpath('./xmlns:uri') unless owner.nil?
- end
-
- raise Goldfinger::Error, 'Author URI could not be found' if author_uri.nil?
- author_uri.content
- end
-
- def get_profile(body, account)
- RemoteProfileUpdateWorker.perform_async(account.id, body.force_encoding('UTF-8'), false)
- end
-end
diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb
@@ -7,7 +7,7 @@ class FollowService < BaseService
# @param [Account] source_account From which to follow
# @param [String] uri User URI to follow in the form of username@domain
def call(source_account, uri)
- target_account = FollowRemoteAccountService.new.call(uri)
+ target_account = ResolveRemoteAccountService.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
@@ -41,6 +41,6 @@ class ProcessMentionsService < BaseService
private
def follow_remote_account_service
- @follow_remote_account_service ||= FollowRemoteAccountService.new
+ @follow_remote_account_service ||= ResolveRemoteAccountService.new
end
end
diff --git a/app/services/resolve_remote_account_service.rb b/app/services/resolve_remote_account_service.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+class ResolveRemoteAccountService < BaseService
+ include OStatus2::MagicKey
+ include HttpHelper
+
+ 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('@')
+
+ return Account.find_local(username) if TagManager.instance.local_domain?(domain)
+
+ account = Account.find_remote(username, domain)
+ return account unless account_needs_webfinger_update?(account)
+
+ Rails.logger.debug "Looking up webfinger for #{uri}"
+
+ data = Goldfinger.finger("acct:#{uri}")
+
+ raise Goldfinger::Error, 'Missing resource links' if data.link('http://schemas.google.com/g/2010#updates-from').nil? || data.link('salmon').nil? || data.link('http://webfinger.net/rel/profile-page').nil? || data.link('magic-public-key').nil?
+
+ # Disallow account hijacking
+ confirmed_username, confirmed_domain = data.subject.gsub(/\Aacct:/, '').split('@')
+
+ unless confirmed_username.casecmp(username).zero? && confirmed_domain.casecmp(domain).zero?
+ return call("#{confirmed_username}@#{confirmed_domain}", update_profile, true) if redirected.nil?
+ raise Goldfinger::Error, 'Requested and returned acct URI do not match'
+ end
+
+ return Account.find_local(confirmed_username) if TagManager.instance.local_domain?(confirmed_domain)
+
+ confirmed_account = Account.find_remote(confirmed_username, confirmed_domain)
+ if confirmed_account.nil?
+ Rails.logger.debug "Creating new remote account for #{uri}"
+
+ domain_block = DomainBlock.find_by(domain: domain)
+ account = Account.new(username: confirmed_username, domain: confirmed_domain)
+ account.suspended = true if domain_block && domain_block.suspend?
+ account.silenced = true if domain_block && domain_block.silence?
+ account.private_key = nil
+ else
+ account = confirmed_account
+ end
+
+ account.last_webfingered_at = Time.now.utc
+
+ account.remote_url = data.link('http://schemas.google.com/g/2010#updates-from').href
+ account.salmon_url = data.link('salmon').href
+ account.url = data.link('http://webfinger.net/rel/profile-page').href
+ account.public_key = magic_key_to_pem(data.link('magic-public-key').href)
+
+ body, xml = get_feed(account.remote_url)
+ hubs = get_hubs(xml)
+
+ account.uri = get_account_uri(xml)
+ account.hub_url = hubs.first.attribute('href').value
+
+ begin
+ account.save!
+ get_profile(body, account) if update_profile
+ rescue ActiveRecord::RecordNotUnique
+ # The account has been added by another worker!
+ return Account.find_remote(confirmed_username, confirmed_domain)
+ end
+
+ account
+ end
+
+ private
+
+ def account_needs_webfinger_update?(account)
+ account&.last_webfingered_at.nil? || account.last_webfingered_at <= 1.day.ago
+ end
+
+ def get_feed(url)
+ response = http_client(write: 20, connect: 20, read: 50).get(Addressable::URI.parse(url).normalize)
+ raise Goldfinger::Error, "Feed attempt failed for #{url}: HTTP #{response.code}" unless response.code == 200
+ [response.to_s, Nokogiri::XML(response)]
+ end
+
+ def get_hubs(xml)
+ hubs = xml.xpath('//xmlns:link[@rel="hub"]')
+ raise Goldfinger::Error, 'No PubSubHubbub hubs found' if hubs.empty? || hubs.first.attribute('href').nil?
+ hubs
+ end
+
+ def get_account_uri(xml)
+ author_uri = xml.at_xpath('/xmlns:feed/xmlns:author/xmlns:uri')
+
+ if author_uri.nil?
+ owner = xml.at_xpath('/xmlns:feed').at_xpath('./dfrn:owner', dfrn: DFRN_NS)
+ author_uri = owner.at_xpath('./xmlns:uri') unless owner.nil?
+ end
+
+ raise Goldfinger::Error, 'Author URI could not be found' if author_uri.nil?
+ author_uri.content
+ end
+
+ def get_profile(body, account)
+ RemoteProfileUpdateWorker.perform_async(account.id, body.force_encoding('UTF-8'), false)
+ end
+end
diff --git a/app/views/activitypub/types/collection.activitystreams2.rabl b/app/views/activitypub/types/collection.activitystreams2.rabl
@@ -1,3 +1,3 @@
extends 'activitypub/intransient.activitystreams2.rabl'
-node(:type) { 'Collection' }
+node(:type) { 'Collection' }
diff --git a/app/views/activitypub/types/ordered_collection_page.activitystreams2.rabl b/app/views/activitypub/types/ordered_collection_page.activitystreams2.rabl
@@ -1,3 +1,3 @@
extends 'activitypub/types/ordered_collection.activitystreams2.rabl'
-node(:type) { 'OrderedCollectionPage' }
+node(:type) { 'OrderedCollectionPage' }
diff --git a/app/workers/import_worker.rb b/app/workers/import_worker.rb
@@ -41,7 +41,7 @@ class ImportWorker
def process_mutes
import_rows.each do |row|
begin
- target_account = FollowRemoteAccountService.new.call(row.first)
+ target_account = ResolveRemoteAccountService.new.call(row.first)
next if target_account.nil?
MuteService.new.call(from_account, target_account)
rescue Goldfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError
@@ -53,7 +53,7 @@ class ImportWorker
def process_blocks
import_rows.each do |row|
begin
- target_account = FollowRemoteAccountService.new.call(row.first)
+ target_account = ResolveRemoteAccountService.new.call(row.first)
next if target_account.nil?
BlockService.new.call(from_account, target_account)
rescue Goldfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError
diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb
@@ -13,4 +13,5 @@
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'StatsD'
inflect.acronym 'OEmbed'
+ inflect.acronym 'ActivityPub'
end
diff --git a/spec/controllers/api/activitypub/activities_controller_spec.rb b/spec/controllers/api/activitypub/activities_controller_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-RSpec.describe Api::Activitypub::ActivitiesController, type: :controller do
+RSpec.describe Api::ActivityPub::ActivitiesController, type: :controller do
render_views
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
diff --git a/spec/controllers/api/activitypub/notes_controller_spec.rb b/spec/controllers/api/activitypub/notes_controller_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-RSpec.describe Api::Activitypub::NotesController, type: :controller do
+RSpec.describe Api::ActivityPub::NotesController, type: :controller do
render_views
let(:user_alice) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
diff --git a/spec/controllers/api/activitypub/outbox_controller_spec.rb b/spec/controllers/api/activitypub/outbox_controller_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-RSpec.describe Api::Activitypub::OutboxController, type: :controller do
+RSpec.describe Api::ActivityPub::OutboxController, type: :controller do
render_views
let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
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(FollowRemoteAccountService).to receive(:new).and_return(service)
+ allow(ResolveRemoteAccountService).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(FollowRemoteAccountService).to receive(:new).and_return(service)
+ allow(ResolveRemoteAccountService).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(FollowRemoteAccountService).to receive(:new).and_return(service)
+ allow(ResolveRemoteAccountService).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(FollowRemoteAccountService).to receive(:new).and_return(service)
+ allow(ResolveRemoteAccountService).to receive(:new).and_return(service)
post :create, params: {
import: {
type: 'blocking',
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(FollowRemoteAccountService).to receive(:new).and_return(service)
+ allow(ResolveRemoteAccountService).to receive(:new).and_return(service)
results = subject.call('newuser@remote.com', 10, 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(FollowRemoteAccountService).to receive(:new).and_return(service)
+ allow(ResolveRemoteAccountService).to receive(:new).and_return(service)
results = subject.call('newuser@remote.com', 10, false)
expect(service).not_to have_received(:call)
diff --git a/spec/services/follow_remote_account_service_spec.rb b/spec/services/follow_remote_account_service_spec.rb
@@ -1,71 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe FollowRemoteAccountService do
- subject { FollowRemoteAccountService.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 raise_error Goldfinger::Error
- end
-
- it 'raises error if the domain does not have webfinger' do
- expect { subject.call('catsrgr8@example.com') }.to raise_error Goldfinger::Error
- end
-
- 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 '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 raise_error Goldfinger::Error
- 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
diff --git a/spec/services/resolve_remote_account_service_spec.rb b/spec/services/resolve_remote_account_service_spec.rb
@@ -0,0 +1,71 @@
+require 'rails_helper'
+
+RSpec.describe ResolveRemoteAccountService do
+ subject { ResolveRemoteAccountService.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 raise_error Goldfinger::Error
+ end
+
+ it 'raises error if the domain does not have webfinger' do
+ expect { subject.call('catsrgr8@example.com') }.to raise_error Goldfinger::Error
+ end
+
+ 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 '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 raise_error Goldfinger::Error
+ 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