diff --git a/lib/diaspora_federation.rb b/lib/diaspora_federation.rb index 1563793..ea23223 100644 --- a/lib/diaspora_federation.rb +++ b/lib/diaspora_federation.rb @@ -29,7 +29,7 @@ module DiasporaFederation fetch_entity_author_id_by_guid queue_public_receive queue_private_receive - save_entity_after_receive + receive_entity fetch_public_entity fetch_person_url_to update_pod @@ -167,9 +167,11 @@ module DiasporaFederation # @param [Boolean] legacy true if it is a legacy salmon slap, false if it is a encrypted magic envelope json # @return [Boolean] true if successful, false if the user was not found # - # save_entity_after_receive + # receive_entity # After the xml was parsed and processed the gem calls this callback to persist the entity # @param [DiasporaFederation::Entity] entity the received entity after processing + # @param [Object] recipient_id identifier for the recipient of private messages or nil for public, + # see {Receiver.receive_private} # # fetch_public_entity # fetch a public entity from the database diff --git a/lib/diaspora_federation/federation/fetcher.rb b/lib/diaspora_federation/federation/fetcher.rb index 4501edb..a985af2 100644 --- a/lib/diaspora_federation/federation/fetcher.rb +++ b/lib/diaspora_federation/federation/fetcher.rb @@ -13,7 +13,7 @@ module DiasporaFederation magic_env = Nokogiri::XML::Document.parse(response.body).root entity = Salmon::MagicEnvelope.unenvelop(magic_env) - DiasporaFederation.callbacks.trigger(:save_entity_after_receive, entity) + DiasporaFederation.callbacks.trigger(:receive_entity, entity) rescue => e raise NotFetchable, "Failed to fetch #{entity_type}:#{guid} from #{author}: #{e.class}: #{e.message}" end diff --git a/lib/diaspora_federation/federation/receiver.rb b/lib/diaspora_federation/federation/receiver.rb index d0975a6..d4fb3e6 100644 --- a/lib/diaspora_federation/federation/receiver.rb +++ b/lib/diaspora_federation/federation/receiver.rb @@ -1,6 +1,36 @@ module DiasporaFederation module Federation + # this module is for parse and receive entities. module Receiver + # receive a public message + # @param [String] data message to receive + # @param [Boolean] legacy use old slap parser + def self.receive_public(data, legacy=false) + receiver = legacy ? PublicSlapReceiver.new(data) : MagicEnvelopeReceiver.new(data) + receive(receiver) + end + + # receive a private message + # @param [String] data message to receive + # @param [OpenSSL::PKey::RSA] recipient_private_key recipient private key to decrypt the message + # @param [Object] recipient_id the identifier to persist the entity for the correct user, + # see +receive_entity+ callback + # @param [Boolean] legacy use old slap parser + def self.receive_private(data, recipient_private_key, recipient_id, legacy=false) + raise ArgumentError, "no recipient key provided" unless recipient_private_key.instance_of?(OpenSSL::PKey::RSA) + receiver = if legacy + PrivateSlapReceiver.new(data, recipient_private_key) + else + EncryptedMagicEnvelopeReceiver.new(data, recipient_private_key) + end + receive(receiver, recipient_id) + end + + def self.receive(receiver, recipient_id=nil) + entity = receiver.parse + DiasporaFederation.callbacks.trigger(:receive_entity, entity, recipient_id) + end + private_class_method :receive end end end @@ -8,3 +38,5 @@ end require "diaspora_federation/federation/receiver/slap_receiver" require "diaspora_federation/federation/receiver/private_slap_receiver" require "diaspora_federation/federation/receiver/public_slap_receiver" +require "diaspora_federation/federation/receiver/magic_envelope_receiver" +require "diaspora_federation/federation/receiver/encrypted_magic_envelope_receiver" diff --git a/lib/diaspora_federation/federation/receiver/encrypted_magic_envelope_receiver.rb b/lib/diaspora_federation/federation/receiver/encrypted_magic_envelope_receiver.rb new file mode 100644 index 0000000..7eb6142 --- /dev/null +++ b/lib/diaspora_federation/federation/receiver/encrypted_magic_envelope_receiver.rb @@ -0,0 +1,24 @@ +module DiasporaFederation + module Federation + module Receiver + # Receiver for an encrypted magic envelope + # + # @see Salmon::EncryptedMagicEnvelope + class EncryptedMagicEnvelopeReceiver < MagicEnvelopeReceiver + # create a new receiver for an encrypted magic envelope + # @param [String] data the encrypted json with magic envelope xml + # @param [OpenSSL::PKey::RSA] recipient_private_key recipient private key to decrypt the message + def initialize(data, recipient_private_key) + super(data) + @recipient_private_key = recipient_private_key + end + + protected + + def magic_env_xml + Salmon::EncryptedMagicEnvelope.decrypt(@data, @recipient_private_key) + end + end + end + end +end diff --git a/lib/diaspora_federation/federation/receiver/magic_envelope_receiver.rb b/lib/diaspora_federation/federation/receiver/magic_envelope_receiver.rb new file mode 100644 index 0000000..1a40762 --- /dev/null +++ b/lib/diaspora_federation/federation/receiver/magic_envelope_receiver.rb @@ -0,0 +1,28 @@ +module DiasporaFederation + module Federation + module Receiver + # Receiver for a magic envelope + # + # @see Salmon::MagicEnvelope + class MagicEnvelopeReceiver + # create a new receiver for a magic envelope + # @param [String] data the message magic envelope xml + def initialize(data) + @data = data + end + + # parse the magic envelope and create the entity + # @return [Entity] the parsed entity + def parse + Salmon::MagicEnvelope.unenvelop(magic_env_xml) + end + + protected + + def magic_env_xml + Nokogiri::XML::Document.parse(@data).root + end + end + end + end +end diff --git a/lib/diaspora_federation/federation/receiver/private_slap_receiver.rb b/lib/diaspora_federation/federation/receiver/private_slap_receiver.rb index 3f309ee..8c2246d 100644 --- a/lib/diaspora_federation/federation/receiver/private_slap_receiver.rb +++ b/lib/diaspora_federation/federation/receiver/private_slap_receiver.rb @@ -10,7 +10,6 @@ module DiasporaFederation # @param [OpenSSL::PKey::RSA] recipient_private_key recipient private key to decrypt the message def initialize(slap_xml, recipient_private_key) super(slap_xml) - raise ArgumentError, "no recipient key provided" unless recipient_private_key.instance_of?(OpenSSL::PKey::RSA) @recipient_private_key = recipient_private_key end diff --git a/lib/diaspora_federation/federation/receiver/slap_receiver.rb b/lib/diaspora_federation/federation/receiver/slap_receiver.rb index 0206a44..76cdb38 100644 --- a/lib/diaspora_federation/federation/receiver/slap_receiver.rb +++ b/lib/diaspora_federation/federation/receiver/slap_receiver.rb @@ -12,12 +12,12 @@ module DiasporaFederation @slap_xml = slap_xml end - # Parse the salmon xml and send it to the +:save_entity_after_receive+ callback - def receive! + # Parse the salmon xml + def parse sender_id = slap.author_id public_key = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, sender_id) raise Salmon::SenderKeyNotFound if public_key.nil? - DiasporaFederation.callbacks.trigger(:save_entity_after_receive, slap.entity(public_key)) + slap.entity(public_key) end end end diff --git a/spec/lib/diaspora_federation/federation/fetcher_spec.rb b/spec/lib/diaspora_federation/federation/fetcher_spec.rb index d9bfe6b..1070107 100644 --- a/spec/lib/diaspora_federation/federation/fetcher_spec.rb +++ b/spec/lib/diaspora_federation/federation/fetcher_spec.rb @@ -15,7 +15,7 @@ module DiasporaFederation :fetch_public_key_by_diaspora_id, post.author ).and_return(alice.public_key) expect(DiasporaFederation.callbacks).to receive(:trigger).with( - :save_entity_after_receive, kind_of(Entities::StatusMessage) + :receive_entity, kind_of(Entities::StatusMessage) ) do |_, entity| expect(entity.guid).to eq(post.guid) expect(entity.author).to eq(post.author) @@ -39,7 +39,7 @@ module DiasporaFederation :fetch_public_key_by_diaspora_id, post.author ).and_return(alice.public_key) expect(DiasporaFederation.callbacks).to receive(:trigger).with( - :save_entity_after_receive, kind_of(Entities::StatusMessage) + :receive_entity, kind_of(Entities::StatusMessage) ) Federation::Fetcher.fetch_public(post.author, :post, post.guid) diff --git a/spec/lib/diaspora_federation/federation/receiver/encrypted_magic_envelope_receiver_spec.rb b/spec/lib/diaspora_federation/federation/receiver/encrypted_magic_envelope_receiver_spec.rb new file mode 100644 index 0000000..01fc7fc --- /dev/null +++ b/spec/lib/diaspora_federation/federation/receiver/encrypted_magic_envelope_receiver_spec.rb @@ -0,0 +1,21 @@ +module DiasporaFederation + describe Federation::Receiver::EncryptedMagicEnvelopeReceiver do + let(:sender_id) { FactoryGirl.generate(:diaspora_id) } + let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) } + let(:recipient_key) { OpenSSL::PKey::RSA.generate(1024) } + let(:entity) { FactoryGirl.build(:status_message_entity, public: false) } + let(:magic_env) { Salmon::MagicEnvelope.new(entity).envelop(sender_key, sender_id) } + let(:data) { Salmon::EncryptedMagicEnvelope.encrypt(magic_env, recipient_key.public_key) } + + it "parses the entity if everything is fine" do + expect(DiasporaFederation.callbacks).to receive(:trigger).with( + :fetch_public_key_by_diaspora_id, sender_id + ).and_return(sender_key) + + parsed_entity = described_class.new(data, recipient_key).parse + expect(parsed_entity).to be_a(Entities::StatusMessage) + expect(parsed_entity.guid).to eq(entity.guid) + expect(parsed_entity.public).to eq("false") + end + end +end diff --git a/spec/lib/diaspora_federation/federation/receiver/magic_envelope_receiver_spec.rb b/spec/lib/diaspora_federation/federation/receiver/magic_envelope_receiver_spec.rb new file mode 100644 index 0000000..bf98b1f --- /dev/null +++ b/spec/lib/diaspora_federation/federation/receiver/magic_envelope_receiver_spec.rb @@ -0,0 +1,19 @@ +module DiasporaFederation + describe Federation::Receiver::MagicEnvelopeReceiver do + let(:sender_id) { FactoryGirl.generate(:diaspora_id) } + let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) } + let(:entity) { FactoryGirl.build(:status_message_entity) } + let(:data) { Salmon::MagicEnvelope.new(entity).envelop(sender_key, sender_id).to_xml } + + it "parses the entity if everything is fine" do + expect(DiasporaFederation.callbacks).to receive(:trigger).with( + :fetch_public_key_by_diaspora_id, sender_id + ).and_return(sender_key) + + parsed_entity = described_class.new(data).parse + expect(parsed_entity).to be_a(Entities::StatusMessage) + expect(parsed_entity.guid).to eq(entity.guid) + expect(parsed_entity.public).to eq("true") + end + end +end diff --git a/spec/lib/diaspora_federation/federation/receiver/private_slap_receiver_spec.rb b/spec/lib/diaspora_federation/federation/receiver/private_slap_receiver_spec.rb index 6273fbb..9435fe6 100644 --- a/spec/lib/diaspora_federation/federation/receiver/private_slap_receiver_spec.rb +++ b/spec/lib/diaspora_federation/federation/receiver/private_slap_receiver_spec.rb @@ -3,19 +3,20 @@ module DiasporaFederation let(:sender_id) { FactoryGirl.generate(:diaspora_id) } let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) } let(:recipient_key) { OpenSSL::PKey::RSA.generate(1024) } + let(:entity) { FactoryGirl.build(:status_message_entity, public: false) } let(:xml) { - DiasporaFederation::Salmon::EncryptedSlap.prepare(sender_id, sender_key, FactoryGirl.build(:request_entity)) - .generate_xml(recipient_key) + DiasporaFederation::Salmon::EncryptedSlap.prepare(sender_id, sender_key, entity).generate_xml(recipient_key) } - it "calls save_entity_after_receive if everything is fine" do + it "parses the entity if everything is fine" do expect(DiasporaFederation.callbacks).to receive(:trigger).with( :fetch_public_key_by_diaspora_id, sender_id ).and_return(sender_key) - expect(DiasporaFederation.callbacks).to receive(:trigger).with(:save_entity_after_receive, kind_of(Entity)) - - described_class.new(xml, recipient_key).receive! + parsed_entity = described_class.new(xml, recipient_key).parse + expect(parsed_entity).to be_a(Entities::StatusMessage) + expect(parsed_entity.guid).to eq(entity.guid) + expect(parsed_entity.public).to eq("false") end it "raises when sender public key is not available" do @@ -24,19 +25,13 @@ module DiasporaFederation ).and_return(nil) expect { - described_class.new(xml, recipient_key).receive! + described_class.new(xml, recipient_key).parse }.to raise_error Salmon::SenderKeyNotFound end - it "raises when recipient private key is not available" do - expect { - described_class.new(xml, nil).receive! - }.to raise_error ArgumentError, "no recipient key provided" - end - it "raises when bad xml was supplied" do expect { - described_class.new("", recipient_key).receive! + described_class.new("", recipient_key).parse }.to raise_error Salmon::MissingHeader end end diff --git a/spec/lib/diaspora_federation/federation/receiver/public_slap_receiver_spec.rb b/spec/lib/diaspora_federation/federation/receiver/public_slap_receiver_spec.rb index 18b249a..e80a1e6 100644 --- a/spec/lib/diaspora_federation/federation/receiver/public_slap_receiver_spec.rb +++ b/spec/lib/diaspora_federation/federation/receiver/public_slap_receiver_spec.rb @@ -2,22 +2,18 @@ module DiasporaFederation describe Federation::Receiver::PublicSlapReceiver do let(:sender_id) { FactoryGirl.generate(:diaspora_id) } let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) } - let(:xml) { - DiasporaFederation::Salmon::Slap.generate_xml( - sender_id, - sender_key, - FactoryGirl.build(:request_entity) - ) - } + let(:entity) { FactoryGirl.build(:status_message_entity) } + let(:xml) { DiasporaFederation::Salmon::Slap.generate_xml(sender_id, sender_key, entity) } - it "calls save_entity_after_receive if everything is fine" do + it "parses the entity if everything is fine" do expect(DiasporaFederation.callbacks).to receive(:trigger).with( :fetch_public_key_by_diaspora_id, sender_id ).and_return(sender_key) - expect(DiasporaFederation.callbacks).to receive(:trigger).with(:save_entity_after_receive, kind_of(Entity)) - - described_class.new(xml).receive! + parsed_entity = described_class.new(xml).parse + expect(parsed_entity).to be_a(Entities::StatusMessage) + expect(parsed_entity.guid).to eq(entity.guid) + expect(parsed_entity.public).to eq("true") end it "raises when sender public key is not available" do @@ -26,13 +22,13 @@ module DiasporaFederation ).and_return(nil) expect { - described_class.new(xml).receive! + described_class.new(xml).parse }.to raise_error Salmon::SenderKeyNotFound end it "raises when bad xml was supplied" do expect { - described_class.new("").receive! + described_class.new("").parse }.to raise_error Salmon::MissingAuthor end end diff --git a/spec/lib/diaspora_federation/federation/receiver_spec.rb b/spec/lib/diaspora_federation/federation/receiver_spec.rb new file mode 100644 index 0000000..9a999fd --- /dev/null +++ b/spec/lib/diaspora_federation/federation/receiver_spec.rb @@ -0,0 +1,102 @@ +module DiasporaFederation + describe Federation::Receiver do + let(:sender_id) { FactoryGirl.generate(:diaspora_id) } + let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) } + let(:recipient_key) { OpenSSL::PKey::RSA.generate(1024) } + + describe ".receive_public" do + let(:post) { FactoryGirl.build(:status_message_entity) } + + it "parses the entity with magic envelope receiver" do + expect(DiasporaFederation.callbacks).to receive(:trigger).with( + :fetch_public_key_by_diaspora_id, sender_id + ).and_return(sender_key) + + data = Salmon::MagicEnvelope.new(post).envelop(sender_key, sender_id).to_xml + + expect(DiasporaFederation.callbacks).to receive(:trigger).with( + :receive_entity, kind_of(Entities::StatusMessage), nil + ) do |_, entity| + expect(entity.guid).to eq(post.guid) + expect(entity.author).to eq(post.author) + expect(entity.raw_message).to eq(post.raw_message) + expect(entity.public).to eq("true") + end + + described_class.receive_public(data) + end + + it "parses the entity with legacy slap receiver" do + expect(DiasporaFederation.callbacks).to receive(:trigger).with( + :fetch_public_key_by_diaspora_id, sender_id + ).and_return(sender_key) + + data = DiasporaFederation::Salmon::Slap.generate_xml(sender_id, sender_key, post) + + expect(DiasporaFederation.callbacks).to receive(:trigger).with( + :receive_entity, kind_of(Entities::StatusMessage), nil + ) do |_, entity| + expect(entity.guid).to eq(post.guid) + expect(entity.author).to eq(post.author) + expect(entity.raw_message).to eq(post.raw_message) + expect(entity.public).to eq("true") + end + + described_class.receive_public(data, true) + end + end + + describe ".receive_private" do + let(:post) { FactoryGirl.build(:status_message_entity, public: false) } + + it "parses the entity with magic envelope receiver" do + expect(DiasporaFederation.callbacks).to receive(:trigger).with( + :fetch_public_key_by_diaspora_id, sender_id + ).and_return(sender_key) + + magic_env = Salmon::MagicEnvelope.new(post).envelop(sender_key, sender_id) + data = Salmon::EncryptedMagicEnvelope.encrypt(magic_env, recipient_key.public_key) + + expect(DiasporaFederation.callbacks).to receive(:trigger).with( + :receive_entity, kind_of(Entities::StatusMessage), 1234 + ) do |_, entity| + expect(entity.guid).to eq(post.guid) + expect(entity.author).to eq(post.author) + expect(entity.raw_message).to eq(post.raw_message) + expect(entity.public).to eq("false") + end + + described_class.receive_private(data, recipient_key, 1234) + end + + it "parses the entity with legacy slap receiver" do + expect(DiasporaFederation.callbacks).to receive(:trigger).with( + :fetch_public_key_by_diaspora_id, sender_id + ).and_return(sender_key) + + data = DiasporaFederation::Salmon::EncryptedSlap.prepare(sender_id, sender_key, post) + .generate_xml(recipient_key) + + expect(DiasporaFederation.callbacks).to receive(:trigger).with( + :receive_entity, kind_of(Entities::StatusMessage), 1234 + ) do |_, entity| + expect(entity.guid).to eq(post.guid) + expect(entity.author).to eq(post.author) + expect(entity.raw_message).to eq(post.raw_message) + expect(entity.public).to eq("false") + end + + described_class.receive_private(data, recipient_key, 1234, true) + end + + it "raises when recipient private key is not available" do + magic_env = Salmon::MagicEnvelope.new(post).envelop(sender_key, sender_id) + data = Salmon::EncryptedMagicEnvelope.encrypt(magic_env, recipient_key.public_key) + + expect { + described_class.receive_private(data, nil, 1234) + }.to raise_error ArgumentError, "no recipient key provided" + end + end + end +end diff --git a/test/dummy/config/initializers/diaspora_federation.rb b/test/dummy/config/initializers/diaspora_federation.rb index 963055c..47fcbbd 100644 --- a/test/dummy/config/initializers/diaspora_federation.rb +++ b/test/dummy/config/initializers/diaspora_federation.rb @@ -97,7 +97,7 @@ DiasporaFederation.configure do |config| true end - on :save_entity_after_receive do + on :receive_entity do end on :fetch_public_entity do |entity_type, guid|