process_collection_service_spec.rb 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. # frozen_string_literal: true
  2. require 'rails_helper'
  3. RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
  4. subject { described_class.new }
  5. let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/account') }
  6. let(:payload) do
  7. {
  8. '@context': 'https://www.w3.org/ns/activitystreams',
  9. id: 'foo',
  10. type: 'Create',
  11. actor: ActivityPub::TagManager.instance.uri_for(actor),
  12. object: {
  13. id: 'bar',
  14. type: 'Note',
  15. content: 'Lorem ipsum',
  16. },
  17. }
  18. end
  19. let(:json) { Oj.dump(payload) }
  20. describe '#call' do
  21. context 'when actor is suspended' do
  22. before do
  23. actor.suspend!(origin: :remote)
  24. end
  25. %w(Accept Add Announce Block Create Flag Follow Like Move Remove).each do |activity_type|
  26. context "with #{activity_type} activity" do
  27. let(:payload) do
  28. {
  29. '@context': 'https://www.w3.org/ns/activitystreams',
  30. id: 'foo',
  31. type: activity_type,
  32. actor: ActivityPub::TagManager.instance.uri_for(actor),
  33. }
  34. end
  35. it 'does not process payload' do
  36. expect(ActivityPub::Activity).to_not receive(:factory)
  37. subject.call(json, actor)
  38. end
  39. end
  40. end
  41. %w(Delete Reject Undo Update).each do |activity_type|
  42. context "with #{activity_type} activity" do
  43. let(:payload) do
  44. {
  45. '@context': 'https://www.w3.org/ns/activitystreams',
  46. id: 'foo',
  47. type: activity_type,
  48. actor: ActivityPub::TagManager.instance.uri_for(actor),
  49. }
  50. end
  51. it 'processes the payload' do
  52. expect(ActivityPub::Activity).to receive(:factory)
  53. subject.call(json, actor)
  54. end
  55. end
  56. end
  57. end
  58. context 'when actor differs from sender' do
  59. let(:forwarder) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/other_account') }
  60. it 'does not process payload if no signature exists' do
  61. allow_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(nil)
  62. expect(ActivityPub::Activity).to_not receive(:factory)
  63. subject.call(json, forwarder)
  64. end
  65. it 'processes payload with actor if valid signature exists' do
  66. payload['signature'] = { 'type' => 'RsaSignature2017' }
  67. allow_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(actor)
  68. expect(ActivityPub::Activity).to receive(:factory).with(instance_of(Hash), actor, instance_of(Hash))
  69. subject.call(json, forwarder)
  70. end
  71. it 'does not process payload if invalid signature exists' do
  72. payload['signature'] = { 'type' => 'RsaSignature2017' }
  73. allow_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(nil)
  74. expect(ActivityPub::Activity).to_not receive(:factory)
  75. subject.call(json, forwarder)
  76. end
  77. context 'when receiving a fabricated status' do
  78. let!(:actor) do
  79. Fabricate(:account,
  80. username: 'bob',
  81. domain: 'example.com',
  82. uri: 'https://example.com/users/bob',
  83. private_key: nil,
  84. public_key: <<~TEXT)
  85. -----BEGIN PUBLIC KEY-----
  86. MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuuYyoyfsRkYnXRotMsId
  87. W3euBDDfiv9oVqOxUVC7bhel8KednIMrMCRWFAkgJhbrlzbIkjVr68o1MP9qLcn7
  88. CmH/BXHp7yhuFTr4byjdJKpwB+/i2jNEsvDH5jR8WTAeTCe0x/QHg21V3F7dSI5m
  89. CCZ/1dSIyOXLRTWVlfDlm3rE4ntlCo+US3/7oSWbg/4/4qEnt1HC32kvklgScxua
  90. 4LR5ATdoXa5bFoopPWhul7MJ6NyWCyQyScUuGdlj8EN4kmKQJvphKHrI9fvhgOuG
  91. TvhTR1S5InA4azSSchY0tXEEw/VNxraeX0KPjbgr6DPcwhPd/m0nhVDq0zVyVBBD
  92. MwIDAQAB
  93. -----END PUBLIC KEY-----
  94. TEXT
  95. end
  96. let(:payload) do
  97. {
  98. '@context': [
  99. 'https://www.w3.org/ns/activitystreams',
  100. nil,
  101. { object: 'https://www.w3.org/ns/activitystreams#object' },
  102. ],
  103. id: 'https://example.com/users/bob/fake-status/activity',
  104. type: 'Create',
  105. actor: 'https://example.com/users/bob',
  106. published: '2022-01-22T15:00:00Z',
  107. to: [
  108. 'https://www.w3.org/ns/activitystreams#Public',
  109. ],
  110. cc: [
  111. 'https://example.com/users/bob/followers',
  112. ],
  113. signature: {
  114. type: 'RsaSignature2017',
  115. creator: 'https://example.com/users/bob#main-key',
  116. created: '2022-03-09T21:57:25Z',
  117. signatureValue: 'WculK0LelTQ0MvGwU9TPoq5pFzFfGYRDCJqjZ232/Udj4' \
  118. 'CHqDTGOSw5UTDLShqBOyycCkbZGrQwXG+dpyDpQLSe1UV' \
  119. 'PZ5TPQtc/9XtI57WlS2nMNpdvRuxGnnb2btPdesXZ7n3p' \
  120. 'Cxo0zjaXrJMe0mqQh5QJO22mahb4bDwwmfTHgbD3nmkD+' \
  121. 'fBfGi+UV2qWwqr+jlV4L4JqNkh0gWljF5KTePLRRZCuWi' \
  122. 'Q/FAt7c67636cdIPf7fR+usjuZltTQyLZKEGuK8VUn2Gk' \
  123. 'fsx5qns7Vcjvlz1JqlAjyO8HPBbzTTHzUG2nUOIgC3Poj' \
  124. 'CSWv6mNTmRGoLZzOscCAYQA6cKw==',
  125. },
  126. '@id': 'https://example.com/users/bob/statuses/107928807471117876/activity',
  127. '@type': 'https://www.w3.org/ns/activitystreams#Create',
  128. 'https://www.w3.org/ns/activitystreams#actor': {
  129. '@id': 'https://example.com/users/bob',
  130. },
  131. 'https://www.w3.org/ns/activitystreams#cc': {
  132. '@id': 'https://example.com/users/bob/followers',
  133. },
  134. object: {
  135. id: 'https://example.com/users/bob/fake-status',
  136. type: 'Note',
  137. published: '2022-01-22T15:00:00Z',
  138. url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=puck-was-here',
  139. attributedTo: 'https://example.com/users/bob',
  140. to: [
  141. 'https://www.w3.org/ns/activitystreams#Public',
  142. ],
  143. cc: [
  144. 'https://example.com/users/bob/followers',
  145. ],
  146. sensitive: false,
  147. atomUri: 'https://example.com/users/bob/fake-status',
  148. conversation: 'tag:example.com,2022-03-09:objectId=15:objectType=Conversation',
  149. content: '<p>puck was here</p>',
  150. '@id': 'https://example.com/users/bob/statuses/107928807471117876',
  151. '@type': 'https://www.w3.org/ns/activitystreams#Note',
  152. 'http://ostatus.org#atomUri': 'https://example.com/users/bob/statuses/107928807471117876',
  153. 'http://ostatus.org#conversation': 'tag:example.com,2022-03-09:objectId=15:objectType=Conversation',
  154. 'https://www.w3.org/ns/activitystreams#attachment': [],
  155. 'https://www.w3.org/ns/activitystreams#attributedTo': {
  156. '@id': 'https://example.com/users/bob',
  157. },
  158. 'https://www.w3.org/ns/activitystreams#cc': {
  159. '@id': 'https://example.com/users/bob/followers',
  160. },
  161. 'https://www.w3.org/ns/activitystreams#content': [
  162. '<p>hello world</p>',
  163. {
  164. '@value': '<p>hello world</p>',
  165. '@language': 'en',
  166. },
  167. ],
  168. 'https://www.w3.org/ns/activitystreams#published': {
  169. '@type': 'http://www.w3.org/2001/XMLSchema#dateTime',
  170. '@value': '2022-03-09T21:55:07Z',
  171. },
  172. 'https://www.w3.org/ns/activitystreams#replies': {
  173. '@id': 'https://example.com/users/bob/statuses/107928807471117876/replies',
  174. '@type': 'https://www.w3.org/ns/activitystreams#Collection',
  175. 'https://www.w3.org/ns/activitystreams#first': {
  176. '@type': 'https://www.w3.org/ns/activitystreams#CollectionPage',
  177. 'https://www.w3.org/ns/activitystreams#items': [],
  178. 'https://www.w3.org/ns/activitystreams#next': {
  179. '@id': 'https://example.com/users/bob/statuses/107928807471117876/replies?only_other_accounts=true&page=true',
  180. },
  181. 'https://www.w3.org/ns/activitystreams#partOf': {
  182. '@id': 'https://example.com/users/bob/statuses/107928807471117876/replies',
  183. },
  184. },
  185. },
  186. 'https://www.w3.org/ns/activitystreams#sensitive': false,
  187. 'https://www.w3.org/ns/activitystreams#tag': [],
  188. 'https://www.w3.org/ns/activitystreams#to': {
  189. '@id': 'https://www.w3.org/ns/activitystreams#Public',
  190. },
  191. 'https://www.w3.org/ns/activitystreams#url': {
  192. '@id': 'https://example.com/@bob/107928807471117876',
  193. },
  194. },
  195. 'https://www.w3.org/ns/activitystreams#published': {
  196. '@type': 'http://www.w3.org/2001/XMLSchema#dateTime',
  197. '@value': '2022-03-09T21:55:07Z',
  198. },
  199. 'https://www.w3.org/ns/activitystreams#to': {
  200. '@id': 'https://www.w3.org/ns/activitystreams#Public',
  201. },
  202. }
  203. end
  204. it 'does not process forged payload' do
  205. expect(ActivityPub::Activity).to_not receive(:factory).with(
  206. hash_including(
  207. 'object' => hash_including(
  208. 'id' => 'https://example.com/users/bob/fake-status'
  209. )
  210. ),
  211. anything,
  212. anything
  213. )
  214. expect(ActivityPub::Activity).to_not receive(:factory).with(
  215. hash_including(
  216. 'object' => hash_including(
  217. 'content' => '<p>puck was here</p>'
  218. )
  219. ),
  220. anything,
  221. anything
  222. )
  223. subject.call(json, forwarder)
  224. expect(Status.where(uri: 'https://example.com/users/bob/fake-status').exists?).to be false
  225. end
  226. end
  227. end
  228. end
  229. end