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.

192 lines
7.7 KiB

  1. require 'rails_helper'
  2. RSpec.describe ResolveAccountService, type: :service do
  3. subject { described_class.new }
  4. before do
  5. stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
  6. stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
  7. stub_request(:get, "https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com").to_return(request_fixture('activitypub-webfinger.txt'))
  8. stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor.txt'))
  9. stub_request(:get, "https://ap.example.com/users/foo.atom").to_return(request_fixture('activitypub-feed.txt'))
  10. stub_request(:get, %r{https://ap.example.com/users/foo/\w+}).to_return(status: 404)
  11. stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:hoge@example.com').to_return(status: 410)
  12. end
  13. context 'when there is an LRDD endpoint but no resolvable account' do
  14. before do
  15. stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
  16. stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:catsrgr8@quitter.no").to_return(status: 404)
  17. end
  18. it 'returns nil' do
  19. expect(subject.call('catsrgr8@quitter.no')).to be_nil
  20. end
  21. end
  22. context 'when there is no LRDD endpoint nor resolvable account' do
  23. before do
  24. stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:catsrgr8@example.com").to_return(status: 404)
  25. end
  26. it 'returns nil' do
  27. expect(subject.call('catsrgr8@example.com')).to be_nil
  28. end
  29. end
  30. context 'when webfinger returns http gone' do
  31. context 'for a previously known account' do
  32. before do
  33. Fabricate(:account, username: 'hoge', domain: 'example.com', last_webfingered_at: nil)
  34. allow(AccountDeletionWorker).to receive(:perform_async)
  35. end
  36. it 'returns nil' do
  37. expect(subject.call('hoge@example.com')).to be_nil
  38. end
  39. it 'queues account deletion worker' do
  40. subject.call('hoge@example.com')
  41. expect(AccountDeletionWorker).to have_received(:perform_async)
  42. end
  43. end
  44. context 'for a previously unknown account' do
  45. it 'returns nil' do
  46. expect(subject.call('hoge@example.com')).to be_nil
  47. end
  48. end
  49. end
  50. context 'with a legitimate webfinger redirection' do
  51. before do
  52. webfinger = { subject: 'acct:foo@ap.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo', type: 'application/activity+json' }] }
  53. stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
  54. end
  55. it 'returns new remote account' do
  56. account = subject.call('Foo@redirected.example.com')
  57. expect(account.activitypub?).to eq true
  58. expect(account.acct).to eq 'foo@ap.example.com'
  59. expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
  60. end
  61. end
  62. context 'with a misconfigured redirection' do
  63. before do
  64. webfinger = { subject: 'acct:Foo@redirected.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo', type: 'application/activity+json' }] }
  65. stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
  66. end
  67. it 'returns new remote account' do
  68. account = subject.call('Foo@redirected.example.com')
  69. expect(account.activitypub?).to eq true
  70. expect(account.acct).to eq 'foo@ap.example.com'
  71. expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
  72. end
  73. end
  74. context 'with too many webfinger redirections' do
  75. before do
  76. webfinger = { subject: 'acct:foo@evil.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo', type: 'application/activity+json' }] }
  77. stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
  78. webfinger2 = { subject: 'acct:foo@ap.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo', type: 'application/activity+json' }] }
  79. stub_request(:get, 'https://evil.example.com/.well-known/webfinger?resource=acct:foo@evil.example.com').to_return(body: Oj.dump(webfinger2), headers: { 'Content-Type': 'application/jrd+json' })
  80. end
  81. it 'returns new remote account' do
  82. expect { subject.call('Foo@redirected.example.com') }.to raise_error Webfinger::RedirectError
  83. end
  84. end
  85. context 'with an ActivityPub account' do
  86. it 'returns new remote account' do
  87. account = subject.call('foo@ap.example.com')
  88. expect(account.activitypub?).to eq true
  89. expect(account.domain).to eq 'ap.example.com'
  90. expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
  91. end
  92. context 'with multiple types' do
  93. before do
  94. stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor-individual.txt'))
  95. end
  96. it 'returns new remote account' do
  97. account = subject.call('foo@ap.example.com')
  98. expect(account.activitypub?).to eq true
  99. expect(account.domain).to eq 'ap.example.com'
  100. expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
  101. expect(account.actor_type).to eq 'Person'
  102. end
  103. end
  104. end
  105. context 'with an already-known actor changing acct: URI' do
  106. let!(:duplicate) { Fabricate(:account, username: 'foo', domain: 'old.example.com', uri: 'https://ap.example.com/users/foo') }
  107. let!(:status) { Fabricate(:status, account: duplicate, text: 'foo') }
  108. it 'returns new remote account' do
  109. account = subject.call('foo@ap.example.com')
  110. expect(account.activitypub?).to eq true
  111. expect(account.domain).to eq 'ap.example.com'
  112. expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
  113. expect(account.uri).to eq 'https://ap.example.com/users/foo'
  114. end
  115. it 'merges accounts' do
  116. account = subject.call('foo@ap.example.com')
  117. expect(status.reload.account_id).to eq account.id
  118. expect(Account.where(uri: account.uri).count).to eq 1
  119. end
  120. end
  121. context 'with an already-known acct: URI changing ActivityPub id' do
  122. let!(:old_account) { Fabricate(:account, username: 'foo', domain: 'ap.example.com', uri: 'https://old.example.com/users/foo', last_webfingered_at: nil) }
  123. let!(:status) { Fabricate(:status, account: old_account, text: 'foo') }
  124. it 'returns new remote account' do
  125. account = subject.call('foo@ap.example.com')
  126. expect(account.activitypub?).to eq true
  127. expect(account.domain).to eq 'ap.example.com'
  128. expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
  129. expect(account.uri).to eq 'https://ap.example.com/users/foo'
  130. end
  131. end
  132. it 'processes one remote account at a time using locks' do
  133. wait_for_start = true
  134. fail_occurred = false
  135. return_values = Concurrent::Array.new
  136. # Preload classes that throw circular dependency errors in threads
  137. Account
  138. TagManager
  139. DomainBlock
  140. threads = Array.new(5) do
  141. Thread.new do
  142. true while wait_for_start
  143. begin
  144. return_values << described_class.new.call('foo@ap.example.com')
  145. rescue ActiveRecord::RecordNotUnique
  146. fail_occurred = true
  147. end
  148. end
  149. end
  150. wait_for_start = false
  151. threads.each(&:join)
  152. expect(fail_occurred).to be false
  153. expect(return_values).to_not include(nil)
  154. end
  155. end