From b6c7f004e42713598cf5fd3152155e8e9d2c408f Mon Sep 17 00:00:00 2001 From: cmrd Senya Date: Mon, 21 Dec 2015 22:20:31 +0300 Subject: [PATCH] Further receive tests development --- lib/diaspora/relayable.rb | 2 +- .../federation/federation_helper.rb | 20 +- .../federation_messages_generation.rb | 164 ++++++++++++ .../receive_federation_messages_spec.rb | 244 +++++++++++------- .../federation/shared_receive_relayable.rb | 121 +++------ .../federation/shared_receive_retraction.rb | 59 ++--- .../federation/shared_receive_stream_items.rb | 166 ++++++++++++ 7 files changed, 547 insertions(+), 229 deletions(-) create mode 100644 spec/integration/federation/federation_messages_generation.rb create mode 100644 spec/integration/federation/shared_receive_stream_items.rb diff --git a/lib/diaspora/relayable.rb b/lib/diaspora/relayable.rb index 3edd989c4..a99ef1a95 100644 --- a/lib/diaspora/relayable.rb +++ b/lib/diaspora/relayable.rb @@ -71,7 +71,7 @@ module Diaspora unless comment_or_like.signature_valid? logger.warn "event=receive status=abort reason='object signature not valid' recipient=#{user.diaspora_handle} "\ - "sender=#{parent.author.diaspora_handle} payload_type=#{self.class} parent_id=#{parent.id}" + "sender=#{comment_or_like.author.diaspora_handle} payload_type=#{self.class} parent_id=#{parent.id}" return end diff --git a/spec/integration/federation/federation_helper.rb b/spec/integration/federation/federation_helper.rb index 5d536098d..6570f7c0a 100644 --- a/spec/integration/federation/federation_helper.rb +++ b/spec/integration/federation/federation_helper.rb @@ -17,10 +17,18 @@ def remote_user_on_pod_c end def generate_xml(entity, remote_user, user) - DiasporaFederation::Salmon::EncryptedSlap.generate_xml( - remote_user.diaspora_handle, - OpenSSL::PKey::RSA.new(remote_user.encryption_key), - entity, - OpenSSL::PKey::RSA.new(user.encryption_key) - ) + if @public + DiasporaFederation::Salmon::Slap.generate_xml( + remote_user.diaspora_handle, + OpenSSL::PKey::RSA.new(remote_user.encryption_key), + entity + ) + else + DiasporaFederation::Salmon::EncryptedSlap.generate_xml( + remote_user.diaspora_handle, + OpenSSL::PKey::RSA.new(remote_user.encryption_key), + entity, + OpenSSL::PKey::RSA.new(user.encryption_key) + ) + end end diff --git a/spec/integration/federation/federation_messages_generation.rb b/spec/integration/federation/federation_messages_generation.rb new file mode 100644 index 000000000..3e6b466db --- /dev/null +++ b/spec/integration/federation/federation_messages_generation.rb @@ -0,0 +1,164 @@ +def generate_profile + @entity = FactoryGirl.build(:profile_entity, diaspora_id: remote_user_on_pod_b.person.diaspora_handle) + + generate_xml(@entity, remote_user_on_pod_b, alice) +end + +def generate_conversation + @entity = FactoryGirl.build( + :conversation_entity, + diaspora_id: remote_user_on_pod_b.diaspora_handle, + participant_ids: "#{remote_user_on_pod_b.diaspora_handle};#{alice.diaspora_handle}" + ) + + generate_xml(@entity, remote_user_on_pod_b, alice) +end + +def generate_status_message + @entity = FactoryGirl.build( + :status_message_entity, + diaspora_id: remote_user_on_pod_b.diaspora_handle, + public: @public + ) + + generate_xml(@entity, remote_user_on_pod_b, alice) +end + +def generate_forged_status_message + substitute_wrong_key(remote_user_on_pod_b, 1) + generate_status_message +end + +def generate_reshare + @entity = FactoryGirl.build( + :reshare_entity, + root_diaspora_id: alice.diaspora_handle, + root_guid: @local_target.guid, + diaspora_id: remote_user_on_pod_b.diaspora_handle, + public: true + ) + + generate_xml(@entity, remote_user_on_pod_b, alice) +end + +def mock_private_key_for_user(user) + expect(DiasporaFederation.callbacks).to receive(:trigger) + .with(:fetch_private_key_by_diaspora_id, user.person.diaspora_handle) + .and_return(user.encryption_key) +end + +def retraction_mock_callbacks(entity, sender) + return unless [ + DiasporaFederation::Entities::SignedRetraction, + DiasporaFederation::Entities::RelayableRetraction + ].include?(entity.class) + + mock_private_key_for_user(sender) + + allow(DiasporaFederation.callbacks).to receive(:trigger) + .with( + :fetch_entity_author_id_by_guid, + entity.target_type, + entity.target_guid + ) + .and_return(sender.encryption_key) +end + +def generate_retraction(entity_name, target_object, sender=remote_user_on_pod_b) + @entity = FactoryGirl.build( + entity_name, + diaspora_id: sender.diaspora_handle, + target_guid: target_object.guid, + target_type: target_object.class.to_s + ) + + retraction_mock_callbacks(@entity, sender) + + generate_xml(@entity, sender, alice) +end + +def generate_forged_retraction(entity_name, target_object, sender=remote_user_on_pod_b) + times = 1 + if %i(signed_retraction_entity relayable_retraction_entity).include?(entity_name) + times += 2 + end + + substitute_wrong_key(sender, times) + generate_retraction(entity_name, target_object, sender) +end + +def generate_relayable_entity(entity_name, target, diaspora_id) + @entity = FactoryGirl.build( + entity_name, + conversation_guid: target.guid, + parent_guid: target.guid, + diaspora_id: diaspora_id, + poll_answer_guid: target.respond_to?(:poll_answers) ? target.poll_answers.first.guid : nil + ) +end + +def mock_entity_author_private_key_unavailable(klass) + expect(DiasporaFederation.callbacks).to receive(:trigger) + .with( + :fetch_author_private_key_by_entity_guid, + klass.get_target_entity_type(@entity.to_h), + kind_of(String) + ) + .and_return(nil) +end + +def mock_entity_author_private_key_as(klass, key) + expect(DiasporaFederation.callbacks).to receive(:trigger) + .with( + :fetch_author_private_key_by_entity_guid, + klass.get_target_entity_type(@entity.to_h), + @remote_target.guid + ) + .and_return(key) +end + +def generate_relayable_local_parent(entity_name) + klass = FactoryGirl.factory_by_name(entity_name).build_class + generate_relayable_entity(entity_name, @local_target, remote_user_on_pod_b.person.diaspora_handle) + + mock_private_key_for_user(remote_user_on_pod_b) + mock_entity_author_private_key_unavailable(klass) + + generate_xml(@entity, remote_user_on_pod_b, alice) +end + +def generate_relayable_remote_parent(entity_name) + klass = FactoryGirl.factory_by_name(entity_name).build_class + generate_relayable_entity(entity_name, @remote_target, remote_user_on_pod_c.person.diaspora_handle) + + mock_private_key_for_user(remote_user_on_pod_c) + mock_entity_author_private_key_as(klass, remote_user_on_pod_b.encryption_key) + + generate_xml(@entity, remote_user_on_pod_b, alice) +end + +def substitute_wrong_key(user, times_number) + expect(user).to receive(:encryption_key).exactly(times_number).times.and_return( + OpenSSL::PKey::RSA.new(1024) + ) +end + +# Checks when a remote pod wants to send us a relayable without having a key for declared diaspora ID +def generate_relayable_local_parent_wrong_author_key(entity_name) + substitute_wrong_key(remote_user_on_pod_b, 2) + generate_relayable_local_parent(entity_name) +end + +# Checks when a remote pod B wants to send us a relayable with authorship from a remote pod C user +# without having correct signature from him. +def generate_relayable_remote_parent_wrong_author_key(entity_name) + substitute_wrong_key(remote_user_on_pod_c, 1) + generate_relayable_remote_parent(entity_name) +end + +# Checks when a remote pod C wants to send us a relayable from its user, but bypassing the pod B where +# remote status came from. +def generate_relayable_remote_parent_wrong_parent_key(entity_name) + substitute_wrong_key(remote_user_on_pod_b, 2) + generate_relayable_remote_parent(entity_name) +end diff --git a/spec/integration/federation/receive_federation_messages_spec.rb b/spec/integration/federation/receive_federation_messages_spec.rb index fdb479dc5..b3697a3ac 100644 --- a/spec/integration/federation/receive_federation_messages_spec.rb +++ b/spec/integration/federation/receive_federation_messages_spec.rb @@ -1,128 +1,176 @@ require "spec_helper" +require "diaspora_federation/test" require "integration/federation/federation_helper" +require "integration/federation/federation_messages_generation" require "integration/federation/shared_receive_relayable" require "integration/federation/shared_receive_retraction" +require "integration/federation/shared_receive_stream_items" -describe Workers::ReceiveEncryptedSalmon do - it "treats sharing request receive correctly" do - entity = FactoryGirl.build(:request_entity, recipient_id: alice.diaspora_handle) +def post_private_message(recipient_guid, xml) + inlined_jobs do + post "/receive/users/#{recipient_guid}", guid: recipient_guid, xml: xml + end +end - expect(Diaspora::Fetcher::Public).to receive(:queue_for) - Workers::ReceiveEncryptedSalmon.new.perform(alice.id, generate_xml(entity, remote_user_on_pod_c, alice)) +def post_public_message(xml) + inlined_jobs do + post "/receive/public", xml: xml + end +end - new_contact = alice.contacts.find {|c| c.person.diaspora_handle == remote_user_on_pod_c.diaspora_handle } - expect(new_contact).not_to be_nil - expect(new_contact.sharing).to eq(true) +def post_message(recipient_guid, xml) + if @public + post_public_message(xml) + else + post_private_message(recipient_guid, xml) + end +end + +def set_up_sharing + contact = alice.contacts.find_or_initialize_by(person_id: remote_user_on_pod_b.person.id) + contact.sharing = true + contact.save +end + +describe "Receive federation messages feature" do + before do + allow(DiasporaFederation.callbacks).to receive(:trigger) + .with(:queue_public_receive, any_args).and_call_original + allow(DiasporaFederation.callbacks).to receive(:trigger) + .with(:queue_private_receive, any_args).and_call_original + allow(DiasporaFederation.callbacks).to receive(:trigger) + .with(:save_person_after_webfinger, any_args).and_call_original end - it "doesn't save the status message if there is no sharing" do - entity = FactoryGirl.build(:status_message_entity, diaspora_id: remote_user_on_pod_b.diaspora_handle, public: false) - Workers::ReceiveEncryptedSalmon.new.perform(alice.id, generate_xml(entity, remote_user_on_pod_b, alice)) - - expect(StatusMessage.exists?(guid: entity.guid)).to be(false) - end - - describe "with messages which require sharing" do + context "with public receive" do before do - contact = alice.contacts.find_or_initialize_by(person_id: remote_user_on_pod_b.person.id) - contact.sharing = true - contact.save + @public = true end - it "treats status message receive correctly" do - entity = FactoryGirl.build(:status_message_entity, - diaspora_id: remote_user_on_pod_b.diaspora_handle, public: false) - Workers::ReceiveEncryptedSalmon.new.perform(alice.id, generate_xml(entity, remote_user_on_pod_b, alice)) + it "receives account deletion correctly" do + post_public_message( + generate_xml( + DiasporaFederation::Entities::AccountDeletion.new(diaspora_id: remote_user_on_pod_b.diaspora_handle), + remote_user_on_pod_b, + nil + ) + ) - expect(StatusMessage.exists?(guid: entity.guid)).to be(true) + expect(AccountDeletion.where(diaspora_handle: remote_user_on_pod_b.diaspora_handle).exists?).to be(true) end - it "doesn't accept status message with wrong signature" do - expect(remote_user_on_pod_b).to receive(:encryption_key).and_return(OpenSSL::PKey::RSA.new(1024)) + it "rejects account deletion with wrong diaspora_id" do + delete_id = FactoryGirl.generate(:diaspora_id) + post_public_message( + generate_xml( + DiasporaFederation::Entities::AccountDeletion.new(diaspora_id: delete_id), + remote_user_on_pod_b, + nil + ) + ) - entity = FactoryGirl.build(:status_message_entity, - diaspora_id: remote_user_on_pod_b.diaspora_handle, public: false) - Workers::ReceiveEncryptedSalmon.new.perform(alice.id, generate_xml(entity, remote_user_on_pod_b, alice)) - - expect(StatusMessage.exists?(guid: entity.guid)).to be(false) + expect(AccountDeletion.where(diaspora_handle: delete_id).exists?).to be(false) + expect(AccountDeletion.where(diaspora_handle: remote_user_on_pod_b.diaspora_handle).exists?).to be(false) end - describe "retractions for non-relayable objects" do - %w( - retraction - signed_retraction - ).each do |retraction_entity_name| - context "with #{retraction_entity_name}" do - %w(status_message photo).each do |target| - context "with #{target}" do - it_behaves_like "it retracts non-relayable object" do - let(:target_object) { FactoryGirl.create(target.to_sym, author: remote_user_on_pod_b.person) } - let(:entity_name) { "#{retraction_entity_name}_entity".to_sym } - end - end - end - end - end + it "reshare of public post passes" do + @local_target = FactoryGirl.create(:status_message, author: alice.person, public: true) + post_public_message(generate_reshare) + + expect( + Reshare.where(root_guid: @local_target.guid, diaspora_handle: remote_user_on_pod_b.diaspora_handle).first + ).not_to be_nil end - describe "with messages which require a status to operate on" do - let(:local_message) { FactoryGirl.create(:status_message, author: alice.person) } - let(:remote_message) { FactoryGirl.create(:status_message, author: remote_user_on_pod_b.person) } + it "reshare of private post fails" do + @local_target = FactoryGirl.create(:status_message, author: alice.person, public: false) + post_public_message(generate_reshare) - %w(comment like participation).each do |entity| - context "with #{entity}" do - it_behaves_like "it deals correctly with a relayable" do - let(:entity_name) { "#{entity}_entity".to_sym } - let(:klass) { entity.camelize.constantize } - end - end + expect( + Reshare.where(root_guid: @local_target.guid, diaspora_handle: remote_user_on_pod_b.diaspora_handle).first + ).to be_nil + end + + it_behaves_like "messages which are indifferent about sharing fact" + + context "with sharing" do + before do + set_up_sharing end - describe "retractions for relayable objects" do - let(:sender) { remote_user_on_pod_b } + it_behaves_like "messages which are indifferent about sharing fact" + it_behaves_like "messages which can't be send without sharing" + end + end - %w( - retraction - signed_retraction - relayable_retraction - ).each do |retraction_entity_name| - context "with #{retraction_entity_name}" do - context "with comment" do - it_behaves_like "it retracts object" do - # case for to-upstream federation - let(:entity_name) { "#{retraction_entity_name}_entity".to_sym } - let(:target_object) { - FactoryGirl.create(:comment, author: remote_user_on_pod_b.person, post: local_message) - } - end + context "with private receive" do + before do + @public = false + end - it_behaves_like "it retracts object" do - # case for to-downsteam federation - let(:entity_name) { "#{retraction_entity_name}_entity".to_sym } - let(:target_object) { - FactoryGirl.create(:comment, author: remote_user_on_pod_c.person, post: remote_message) - } - end - end + it "treats sharing request recive correctly" do + entity = FactoryGirl.build(:request_entity, recipient_id: alice.diaspora_handle) - context "with like" do - it_behaves_like "it retracts object" do - # case for to-upstream federation - let(:entity_name) { "#{retraction_entity_name}_entity".to_sym } - let(:target_object) { - FactoryGirl.create(:like, author: remote_user_on_pod_b.person, target: local_message) - } - end + expect(Diaspora::Fetcher::Public).to receive(:queue_for).exactly(1).times - it_behaves_like "it retracts object" do - # case for to-downsteam federation - let(:entity_name) { "#{retraction_entity_name}_entity".to_sym } - let(:target_object) { - FactoryGirl.create(:like, author: remote_user_on_pod_c.person, target: remote_message) - } - end - end - end + post_private_message(alice.guid, generate_xml(entity, remote_user_on_pod_b, alice)) + + expect(alice.contacts.count).to eq(2) + new_contact = alice.contacts.order(created_at: :asc).last + expect(new_contact).not_to be_nil + expect(new_contact.sharing).to eq(true) + expect(new_contact.person.diaspora_handle).to eq(remote_user_on_pod_b.diaspora_handle) + + expect( + Notifications::StartedSharing.where( + recipient_id: alice.id, + target_type: "Person", + target_id: remote_user_on_pod_b.person.id + ).first + ).not_to be_nil + end + + it "doesn't save the private status message if there is no sharing" do + post_private_message(alice.guid, generate_status_message) + + expect(StatusMessage.exists?(guid: @entity.guid)).to be(false) + end + + context "with sharing" do + before do + set_up_sharing + end + + it_behaves_like "messages which are indifferent about sharing fact" + it_behaves_like "messages which can't be send without sharing" + + it "treats profile receive correctly" do + post_private_message(alice.guid, generate_profile) + + expect(Profile.where(diaspora_handle: @entity.diaspora_id).exists?).to be(true) + end + + it "receives conversation correctly" do + post_private_message(alice.guid, generate_conversation) + + expect(Conversation.exists?(guid: @entity.guid)).to be(true) + end + + context "with message" do + before do + @local_target = FactoryGirl.build(:conversation, author: alice.person) + @local_target.participants << remote_user_on_pod_b.person + @local_target.participants << remote_user_on_pod_c.person + @local_target.save + @remote_target = FactoryGirl.build(:conversation, author: remote_user_on_pod_b.person) + @remote_target.participants << alice.person + @remote_target.participants << remote_user_on_pod_c.person + @remote_target.save + end + + it_behaves_like "it deals correctly with a relayable" do + let(:entity_name) { :message_entity } + let(:klass) { Message } end end end diff --git a/spec/integration/federation/shared_receive_relayable.rb b/spec/integration/federation/shared_receive_relayable.rb index 519a2793b..7a8f25fa9 100644 --- a/spec/integration/federation/shared_receive_relayable.rb +++ b/spec/integration/federation/shared_receive_relayable.rb @@ -1,93 +1,44 @@ shared_examples_for "it deals correctly with a relayable" do - context "local" do - let(:entity) { - FactoryGirl.build( - entity_name, - parent_guid: local_message.guid, - diaspora_id: remote_user_on_pod_b.diaspora_handle - ) - } - - def mock_private_keys - allow(DiasporaFederation.callbacks).to receive(:trigger) - .with(:fetch_private_key_by_diaspora_id, - remote_user_on_pod_b.diaspora_handle) - .and_return(remote_user_on_pod_b.encryption_key) - allow(DiasporaFederation.callbacks).to receive(:trigger) - .with(:fetch_author_private_key_by_entity_guid, "Post", kind_of(String)) - .and_return(nil) - end - - it "treats upstream receive correctly" do - mock_private_keys - - Workers::ReceiveEncryptedSalmon.new.perform(alice.id, generate_xml(entity, remote_user_on_pod_b, alice)) - received_entity = klass.find_by(guid: entity.guid) - expect(received_entity).not_to be_nil - expect(received_entity.author.diaspora_handle).to eq(remote_user_on_pod_b.person.diaspora_handle) - end - - # Checks when a remote pod wants to send us a relayable without having a key for declared diaspora ID - it "rejects an upstream entity with a malformed author signature" do - allow(remote_user_on_pod_b).to receive(:encryption_key).and_return(OpenSSL::PKey::RSA.new(1024)) - mock_private_keys - - Workers::ReceiveEncryptedSalmon.new.perform(alice.id, generate_xml(entity, remote_user_on_pod_b, alice)) - expect(klass.exists?(guid: entity.guid)).to be(false) - end + it "treats upstream receive correctly" do + expect(Postzord::Dispatcher).to receive(:build).with(alice, kind_of(klass)).and_call_original + post_message(alice.guid, generate_relayable_local_parent(entity_name)) + received_entity = klass.find_by(guid: @entity.guid) + expect(received_entity).not_to be_nil + expect(received_entity.author.diaspora_handle).to eq(remote_user_on_pod_b.person.diaspora_handle) end - context "remote parent" do - let(:entity) { - FactoryGirl.build( - entity_name, - parent_guid: remote_message.guid, - diaspora_id: remote_user_on_pod_c.diaspora_handle - ) - } + it "rejects an upstream entity with a malformed author signature" do + expect(Postzord::Dispatcher).not_to receive(:build) + post_message( + alice.guid, + generate_relayable_local_parent_wrong_author_key(entity_name) + ) + expect(klass.exists?(guid: @entity.guid)).to be(false) + end - def mock_private_keys - allow(DiasporaFederation.callbacks).to receive(:trigger) - .with(:fetch_private_key_by_diaspora_id, - remote_user_on_pod_c.diaspora_handle) - .and_return(remote_user_on_pod_c.encryption_key) + it "treats downstream receive correctly" do + expect(Postzord::Dispatcher).to receive(:build).with(alice, kind_of(klass)).and_call_original unless @public + post_message(alice.guid, generate_relayable_remote_parent(entity_name)) + received_entity = klass.find_by(guid: @entity.guid) + expect(received_entity).not_to be_nil + expect(received_entity.author.diaspora_handle).to eq(remote_user_on_pod_c.person.diaspora_handle) + end - allow(DiasporaFederation.callbacks).to receive(:trigger) - .with( - :fetch_author_private_key_by_entity_guid, - "Post", - remote_message.guid - ) - .and_return(remote_user_on_pod_b.encryption_key) - end + it "rejects a downstream entity with a malformed author signature" do + expect(Postzord::Dispatcher).not_to receive(:build) + post_message( + alice.guid, + generate_relayable_remote_parent_wrong_author_key(entity_name) + ) + expect(klass.exists?(guid: @entity.guid)).to be(false) + end - it "treats downstream receive correctly" do - mock_private_keys - - Workers::ReceiveEncryptedSalmon.new.perform(alice.id, generate_xml(entity, remote_user_on_pod_b, alice)) - received_entity = klass.find_by(guid: entity.guid) - expect(received_entity).not_to be_nil - expect(received_entity.author.diaspora_handle).to eq(remote_user_on_pod_c.diaspora_handle) - end - - # Checks when a remote pod B wants to send us a relayable with authorship from a remote pod C user - # without having correct signature from him. - it "rejects a downstream entity with a malformed author signature" do - allow(remote_user_on_pod_c).to receive(:encryption_key).and_return(OpenSSL::PKey::RSA.new(1024)) - mock_private_keys - - Workers::ReceiveEncryptedSalmon.new.perform(alice.id, generate_xml(entity, remote_user_on_pod_b, alice)) - expect(klass.exists?(guid: entity.guid)).to be(false) - end - - # Checks when a remote pod C wants to send us a relayable from its user, but bypassing the pod B where - # remote status came from. - it "declines downstream receive when sender signed with a wrong key" do - allow(remote_user_on_pod_b).to receive(:encryption_key).and_return(OpenSSL::PKey::RSA.new(1024)) - mock_private_keys - - Workers::ReceiveEncryptedSalmon.new.perform(alice.id, generate_xml(entity, remote_user_on_pod_b, alice)) - expect(klass.exists?(guid: entity.guid)).to be(false) - end + it "declines downstream receive when sender signed with a wrong key" do + expect(Postzord::Dispatcher).not_to receive(:build) + post_message( + alice.guid, + generate_relayable_remote_parent_wrong_parent_key(entity_name) + ) + expect(klass.exists?(guid: @entity.guid)).to be(false) end end diff --git a/spec/integration/federation/shared_receive_retraction.rb b/spec/integration/federation/shared_receive_retraction.rb index 4264bc1ad..71ae13905 100644 --- a/spec/integration/federation/shared_receive_retraction.rb +++ b/spec/integration/federation/shared_receive_retraction.rb @@ -1,41 +1,22 @@ -def mock_private_keys_for_retraction(entity_name, entity, sender) - if %i(signed_retraction_entity relayable_retraction_entity).include?(entity_name) - allow(DiasporaFederation.callbacks).to receive(:trigger) - .with(:fetch_private_key_by_diaspora_id, sender.diaspora_handle) - .and_return(sender.encryption_key) - end - if entity_name == :relayable_retraction_entity - allow(DiasporaFederation.callbacks).to receive(:trigger) - .with( - :fetch_entity_author_id_by_guid, - entity.target_type, - entity.target_guid - ) - .and_return(sender.encryption_key) - end -end - -def generate_retraction(entity_name, target_object, sender) - entity = FactoryGirl.build( - entity_name, - diaspora_id: sender.diaspora_handle, - target_guid: target_object.guid, - target_type: target_object.class.to_s - ) - - mock_private_keys_for_retraction(entity_name, entity, sender) - generate_xml(entity, sender, alice) -end - shared_examples_for "it retracts non-relayable object" do - it_behaves_like "it retracts object" do - let(:sender) { remote_user_on_pod_b } + it "retracts object by a correct retraction message" do + target_klass = target_object.class.to_s.constantize + post_message(alice.guid, generate_retraction(entity_name, target_object)) + + expect(target_klass.exists?(guid: target_object.guid)).to be(false) + end + + it "doesn't retract object when retraction has wrong signatures" do + target_klass = target_object.class.to_s.constantize + post_message(alice.guid, generate_forged_retraction(entity_name, target_object)) + + expect(target_klass.exists?(guid: target_object.guid)).to be(true) end it "doesn't retract object when sender is different from target object" do target_klass = target_object.class.to_s.constantize - Workers::ReceiveEncryptedSalmon.new.perform( - alice.id, + post_message( + alice.guid, generate_retraction(entity_name, target_object, remote_user_on_pod_c) ) @@ -43,20 +24,20 @@ shared_examples_for "it retracts non-relayable object" do end end -shared_examples_for "it retracts object" do +shared_examples_for "it retracts relayable object" do it "retracts object by a correct message" do target_klass = target_object.class.to_s.constantize - Workers::ReceiveEncryptedSalmon.new.perform(alice.id, generate_retraction(entity_name, target_object, sender)) + post_message(alice.guid, generate_retraction(entity_name, target_object, sender)) expect(target_klass.exists?(guid: target_object.guid)).to be(false) end it "doesn't retract object when retraction has wrong signatures" do target_klass = target_object.class.to_s.constantize - - allow(sender).to receive(:encryption_key).and_return(OpenSSL::PKey::RSA.new(1024)) - - Workers::ReceiveEncryptedSalmon.new.perform(alice.id, generate_retraction(entity_name, target_object, sender)) + post_message( + alice.guid, + generate_forged_retraction(entity_name, target_object, sender) + ) expect(target_klass.exists?(guid: target_object.guid)).to be(true) end diff --git a/spec/integration/federation/shared_receive_stream_items.rb b/spec/integration/federation/shared_receive_stream_items.rb new file mode 100644 index 000000000..109488bcc --- /dev/null +++ b/spec/integration/federation/shared_receive_stream_items.rb @@ -0,0 +1,166 @@ +# by "stream items" we mean everything that could appear in the stream - post, comment, like, poll, etc and therefore +# could be send either publicly or privately +def set_up_messages + @local_target = FactoryGirl.create(:status_message, author: alice.person, public: @public) + @remote_target = FactoryGirl.create(:status_message, author: remote_user_on_pod_b.person, public: @public) +end + +shared_examples_for "messages which are indifferent about sharing fact" do + it "treats status message receive correctly" do + post_message(alice.guid, generate_status_message) + + expect(StatusMessage.exists?(guid: @entity.guid)).to be(true) + end + + it "doesn't accept status message with wrong signature" do + post_message(alice.guid, generate_forged_status_message) + + expect(StatusMessage.exists?(guid: @entity.guid)).to be(false) + end + + describe "with messages which require a status to operate on" do + before do + set_up_messages + end + + describe "notifications are sent where required" do + it "for comment on local post" do + post_message(alice.guid, generate_relayable_local_parent(:comment_entity)) + + expect( + Notifications::CommentOnPost.where( + recipient_id: alice.id, + target_type: "Post", + target_id: @local_target.id + ).first + ).not_to be_nil + end + + it "for like on local post" do + post_message(alice.guid, generate_relayable_local_parent(:like_entity)) + + expect( + Notifications::Liked.where( + recipient_id: alice.id, + target_type: "Post", + target_id: @local_target.id + ).first + ).not_to be_nil + end + end + + %w(comment like participation).each do |entity| + context "with #{entity}" do + it_behaves_like "it deals correctly with a relayable" do + let(:entity_name) { "#{entity}_entity".to_sym } + let(:klass) { entity.camelize.constantize } + end + end + end + + context "with poll_participation" do + before do + @local_target = FactoryGirl.create(:poll, status_message: @local_target) + @remote_target = FactoryGirl.create(:poll, status_message: @remote_target) + end + + it_behaves_like "it deals correctly with a relayable" do + let(:entity_name) { :poll_participation_entity } + let(:klass) { PollParticipation } + end + end + end +end + +shared_examples_for "messages which can't be send without sharing" do + # retractions shouldn't depend on sharing fact + describe "retractions for non-relayable objects" do + %w( + retraction + signed_retraction + ).each do |retraction_entity_name| + context "with #{retraction_entity_name}" do + %w(status_message photo).each do |target| + context "with #{target}" do + it_behaves_like "it retracts non-relayable object" do + let(:target_object) { FactoryGirl.create(target.to_sym, author: remote_user_on_pod_b.person) } + let(:entity_name) { "#{retraction_entity_name}_entity".to_sym } + end + end + end + end + end + end + + describe "with messages which require a status to operate on" do + before do + set_up_messages + end + + # this one shouldn't depend on the sharing fact. this must be fixed + describe "notifications are sent where required" do + it "for comment on remote post where we participate" do + alice.participate!(@remote_target) + post_message(alice.guid, generate_relayable_remote_parent(:comment_entity)) + + expect( + Notifications::AlsoCommented.where( + recipient_id: alice.id, + target_type: "Post", + target_id: @remote_target.id + ).first + ).not_to be_nil + end + end + + describe "retractions for relayable objects" do + %w( + retraction + signed_retraction + relayable_retraction + ).each do |retraction_entity_name| + context "with #{retraction_entity_name}" do + context "with comment" do + it_behaves_like "it retracts relayable object" do + # case for to-upstream federation + let(:entity_name) { "#{retraction_entity_name}_entity".to_sym } + let(:target_object) { + FactoryGirl.create(:comment, author: remote_user_on_pod_b.person, post: @local_target) + } + let(:sender) { remote_user_on_pod_b } + end + + it_behaves_like "it retracts relayable object" do + # case for to-downsteam federation + let(:target_object) { + FactoryGirl.create(:comment, author: remote_user_on_pod_c.person, post: @remote_target) + } + let(:entity_name) { "#{retraction_entity_name}_entity".to_sym } + let(:sender) { remote_user_on_pod_b } + end + end + + context "with like" do + it_behaves_like "it retracts relayable object" do + # case for to-upstream federation + let(:entity_name) { "#{retraction_entity_name}_entity".to_sym } + let(:target_object) { + FactoryGirl.create(:like, author: remote_user_on_pod_b.person, target: @local_target) + } + let(:sender) { remote_user_on_pod_b } + end + + it_behaves_like "it retracts relayable object" do + # case for to-downsteam federation + let(:target_object) { + FactoryGirl.create(:like, author: remote_user_on_pod_c.person, target: @remote_target) + } + let(:entity_name) { "#{retraction_entity_name}_entity".to_sym } + let(:sender) { remote_user_on_pod_b } + end + end + end + end + end + end +end