From 20675b941a7d3f539955dab725c43fb3a007bbf8 Mon Sep 17 00:00:00 2001 From: Benjamin Neff Date: Mon, 21 Mar 2016 01:01:55 +0100 Subject: [PATCH] create public/private receiver --- lib/diaspora_federation/federation/fetcher.rb | 2 +- .../federation/receiver.rb | 38 +++++++++---------- .../federation/receiver/abstract_receiver.rb | 35 +++++++++++++++++ .../federation/receiver/exceptions.rb | 13 +++++++ .../federation/receiver/private.rb | 15 ++++++++ .../federation/receiver/public.rb | 9 +++++ .../federation/fetcher_spec.rb | 27 ++++++++----- .../federation/receiver/private_spec.rb | 30 +++++++++++++++ .../federation/receiver/public_spec.rb | 23 +++++++++++ .../federation/receiver_spec.rb | 19 +++++----- 10 files changed, 171 insertions(+), 40 deletions(-) create mode 100644 lib/diaspora_federation/federation/receiver/abstract_receiver.rb create mode 100644 lib/diaspora_federation/federation/receiver/exceptions.rb create mode 100644 lib/diaspora_federation/federation/receiver/private.rb create mode 100644 lib/diaspora_federation/federation/receiver/public.rb create mode 100644 spec/lib/diaspora_federation/federation/receiver/private_spec.rb create mode 100644 spec/lib/diaspora_federation/federation/receiver/public_spec.rb diff --git a/lib/diaspora_federation/federation/fetcher.rb b/lib/diaspora_federation/federation/fetcher.rb index db63c56..824e2a0 100644 --- a/lib/diaspora_federation/federation/fetcher.rb +++ b/lib/diaspora_federation/federation/fetcher.rb @@ -13,7 +13,7 @@ module DiasporaFederation magic_env_xml = Nokogiri::XML::Document.parse(response.body).root magic_env = Salmon::MagicEnvelope.unenvelop(magic_env_xml) - DiasporaFederation.callbacks.trigger(:receive_entity, magic_env.payload) + Receiver::Public.new(magic_env).receive 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 94a26f9..d62398c 100644 --- a/lib/diaspora_federation/federation/receiver.rb +++ b/lib/diaspora_federation/federation/receiver.rb @@ -6,13 +6,13 @@ module DiasporaFederation # @param [String] data message to receive # @param [Boolean] legacy use old slap parser def self.receive_public(data, legacy=false) - received_message = if legacy - Salmon::Slap.from_xml(data) - else - magic_env_xml = Nokogiri::XML::Document.parse(data).root - Salmon::MagicEnvelope.unenvelop(magic_env_xml) - end - receive(received_message) + magic_env = if legacy + Salmon::Slap.from_xml(data) + else + magic_env_xml = Nokogiri::XML::Document.parse(data).root + Salmon::MagicEnvelope.unenvelop(magic_env_xml) + end + Public.new(magic_env).receive end # receive a private message @@ -23,19 +23,19 @@ module DiasporaFederation # @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) - received_message = if legacy - Salmon::EncryptedSlap.from_xml(data, recipient_private_key) - else - magic_env_xml = Salmon::EncryptedMagicEnvelope.decrypt(data, recipient_private_key) - Salmon::MagicEnvelope.unenvelop(magic_env_xml) - end - receive(received_message, recipient_id) + magic_env = if legacy + Salmon::EncryptedSlap.from_xml(data, recipient_private_key) + else + magic_env_xml = Salmon::EncryptedMagicEnvelope.decrypt(data, recipient_private_key) + Salmon::MagicEnvelope.unenvelop(magic_env_xml) + end + Private.new(magic_env, recipient_id).receive end - - def self.receive(received_message, recipient_id=nil) - DiasporaFederation.callbacks.trigger(:receive_entity, received_message.payload, recipient_id) - end - private_class_method :receive end end end + +require "diaspora_federation/federation/receiver/exceptions" +require "diaspora_federation/federation/receiver/abstract_receiver" +require "diaspora_federation/federation/receiver/public" +require "diaspora_federation/federation/receiver/private" diff --git a/lib/diaspora_federation/federation/receiver/abstract_receiver.rb b/lib/diaspora_federation/federation/receiver/abstract_receiver.rb new file mode 100644 index 0000000..8dc6207 --- /dev/null +++ b/lib/diaspora_federation/federation/receiver/abstract_receiver.rb @@ -0,0 +1,35 @@ +module DiasporaFederation + module Federation + module Receiver + # common functionality for receivers + class AbstractReceiver + # create a new receiver + # @param [MagicEnvelope] magic_envelope the received magic envelope + # @param [Object] recipient_id the identifier of the recipient of a private message + def initialize(magic_envelope, recipient_id=nil) + @entity = magic_envelope.payload + @sender = magic_envelope.sender + @recipient_id = recipient_id + end + + # validate and receive the entity + def receive + validate + DiasporaFederation.callbacks.trigger(:receive_entity, entity, recipient_id) + end + + private + + attr_reader :entity, :sender, :recipient_id + + def validate + raise InvalidSender unless sender_valid? + end + + def sender_valid? + sender == entity.author # TODO: handle sender of relayables + end + end + end + end +end diff --git a/lib/diaspora_federation/federation/receiver/exceptions.rb b/lib/diaspora_federation/federation/receiver/exceptions.rb new file mode 100644 index 0000000..65395ca --- /dev/null +++ b/lib/diaspora_federation/federation/receiver/exceptions.rb @@ -0,0 +1,13 @@ +module DiasporaFederation + module Federation + module Receiver + # Raised, if the sender of the {Salmon::MagicEnvelope} is not allowed to send the entity. + class InvalidSender < RuntimeError + end + + # Raised, if receiving a private message without recipient. + class RecipientRequired < RuntimeError + end + end + end +end diff --git a/lib/diaspora_federation/federation/receiver/private.rb b/lib/diaspora_federation/federation/receiver/private.rb new file mode 100644 index 0000000..b6177dc --- /dev/null +++ b/lib/diaspora_federation/federation/receiver/private.rb @@ -0,0 +1,15 @@ +module DiasporaFederation + module Federation + module Receiver + # receiver for private entities + class Private < AbstractReceiver + private + + def validate + raise RecipientRequired if recipient_id.nil? + super + end + end + end + end +end diff --git a/lib/diaspora_federation/federation/receiver/public.rb b/lib/diaspora_federation/federation/receiver/public.rb new file mode 100644 index 0000000..ffc53cb --- /dev/null +++ b/lib/diaspora_federation/federation/receiver/public.rb @@ -0,0 +1,9 @@ +module DiasporaFederation + module Federation + module Receiver + # receiver for public entities + class Public < AbstractReceiver + end + end + end +end diff --git a/spec/lib/diaspora_federation/federation/fetcher_spec.rb b/spec/lib/diaspora_federation/federation/fetcher_spec.rb index 45251f1..b334912 100644 --- a/spec/lib/diaspora_federation/federation/fetcher_spec.rb +++ b/spec/lib/diaspora_federation/federation/fetcher_spec.rb @@ -14,14 +14,18 @@ module DiasporaFederation expect(DiasporaFederation.callbacks).to receive(:trigger).with( :fetch_public_key_by_diaspora_id, post.author ).and_return(alice.public_key) - expect(DiasporaFederation.callbacks).to receive(:trigger).with( - :receive_entity, kind_of(Entities::StatusMessage) - ) 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") + + receiver = double + expect(Federation::Receiver::Public).to receive(:new).with( + kind_of(Salmon::MagicEnvelope) + ) do |magic_env| + expect(magic_env.payload.guid).to eq(post.guid) + expect(magic_env.payload.author).to eq(post.author) + expect(magic_env.payload.raw_message).to eq(post.raw_message) + expect(magic_env.payload.public).to eq("true") + receiver end + expect(receiver).to receive(:receive) Federation::Fetcher.fetch_public(post.author, :post, post.guid) end @@ -38,9 +42,12 @@ module DiasporaFederation expect(DiasporaFederation.callbacks).to receive(:trigger).with( :fetch_public_key_by_diaspora_id, post.author ).and_return(alice.public_key) - expect(DiasporaFederation.callbacks).to receive(:trigger).with( - :receive_entity, kind_of(Entities::StatusMessage) - ) + + receiver = double + expect(Federation::Receiver::Public).to receive(:new).with( + kind_of(Salmon::MagicEnvelope) + ).and_return(receiver) + expect(receiver).to receive(:receive) Federation::Fetcher.fetch_public(post.author, :post, post.guid) end diff --git a/spec/lib/diaspora_federation/federation/receiver/private_spec.rb b/spec/lib/diaspora_federation/federation/receiver/private_spec.rb new file mode 100644 index 0000000..bf56f20 --- /dev/null +++ b/spec/lib/diaspora_federation/federation/receiver/private_spec.rb @@ -0,0 +1,30 @@ +module DiasporaFederation + describe Federation::Receiver::Private do + let(:recipient) { 42 } + let(:entity) { FactoryGirl.build(:status_message_entity, public: false) } + let(:magic_env) { Salmon::MagicEnvelope.new(entity, entity.author) } + + describe "#receive" do + it "receives a private post" do + expect(DiasporaFederation.callbacks).to receive(:trigger).with(:receive_entity, entity, recipient) + + described_class.new(magic_env, recipient).receive + end + + it "validates the sender" do + sender = FactoryGirl.generate(:diaspora_id) + bad_env = Salmon::MagicEnvelope.new(entity, sender) + + expect { + described_class.new(bad_env, recipient).receive + }.to raise_error Federation::Receiver::InvalidSender + end + + it "validates the recipient" do + expect { + described_class.new(magic_env).receive + }.to raise_error Federation::Receiver::RecipientRequired + end + end + end +end diff --git a/spec/lib/diaspora_federation/federation/receiver/public_spec.rb b/spec/lib/diaspora_federation/federation/receiver/public_spec.rb new file mode 100644 index 0000000..f6bbec6 --- /dev/null +++ b/spec/lib/diaspora_federation/federation/receiver/public_spec.rb @@ -0,0 +1,23 @@ +module DiasporaFederation + describe Federation::Receiver::Public do + let(:entity) { FactoryGirl.build(:status_message_entity) } + let(:magic_env) { Salmon::MagicEnvelope.new(entity, entity.author) } + + describe "#receive" do + it "receives a public post" do + expect(DiasporaFederation.callbacks).to receive(:trigger).with(:receive_entity, entity, nil) + + described_class.new(magic_env).receive + end + + it "validates the sender" do + sender = FactoryGirl.generate(:diaspora_id) + bad_env = Salmon::MagicEnvelope.new(entity, sender) + + expect { + described_class.new(bad_env).receive + }.to raise_error Federation::Receiver::InvalidSender + end + end + end +end diff --git a/spec/lib/diaspora_federation/federation/receiver_spec.rb b/spec/lib/diaspora_federation/federation/receiver_spec.rb index d43993e..a0ac95c 100644 --- a/spec/lib/diaspora_federation/federation/receiver_spec.rb +++ b/spec/lib/diaspora_federation/federation/receiver_spec.rb @@ -1,6 +1,5 @@ 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) } @@ -9,10 +8,10 @@ module DiasporaFederation it "parses the entity with magic envelope receiver" do expect(DiasporaFederation.callbacks).to receive(:trigger).with( - :fetch_public_key_by_diaspora_id, sender_id + :fetch_public_key_by_diaspora_id, post.author ).and_return(sender_key) - data = Salmon::MagicEnvelope.new(post, sender_id).envelop(sender_key).to_xml + data = Salmon::MagicEnvelope.new(post, post.author).envelop(sender_key).to_xml expect(DiasporaFederation.callbacks).to receive(:trigger).with( :receive_entity, kind_of(Entities::StatusMessage), nil @@ -28,10 +27,10 @@ module DiasporaFederation it "parses the entity with legacy slap receiver" do expect(DiasporaFederation.callbacks).to receive(:trigger).with( - :fetch_public_key_by_diaspora_id, sender_id + :fetch_public_key_by_diaspora_id, post.author ).and_return(sender_key) - data = DiasporaFederation::Salmon::Slap.generate_xml(sender_id, sender_key, post) + data = DiasporaFederation::Salmon::Slap.generate_xml(post.author, sender_key, post) expect(DiasporaFederation.callbacks).to receive(:trigger).with( :receive_entity, kind_of(Entities::StatusMessage), nil @@ -51,10 +50,10 @@ module DiasporaFederation it "parses the entity with magic envelope receiver" do expect(DiasporaFederation.callbacks).to receive(:trigger).with( - :fetch_public_key_by_diaspora_id, sender_id + :fetch_public_key_by_diaspora_id, post.author ).and_return(sender_key) - magic_env = Salmon::MagicEnvelope.new(post, sender_id).envelop(sender_key) + magic_env = Salmon::MagicEnvelope.new(post, post.author).envelop(sender_key) data = Salmon::EncryptedMagicEnvelope.encrypt(magic_env, recipient_key.public_key) expect(DiasporaFederation.callbacks).to receive(:trigger).with( @@ -71,10 +70,10 @@ module DiasporaFederation it "parses the entity with legacy slap receiver" do expect(DiasporaFederation.callbacks).to receive(:trigger).with( - :fetch_public_key_by_diaspora_id, sender_id + :fetch_public_key_by_diaspora_id, post.author ).and_return(sender_key) - data = DiasporaFederation::Salmon::EncryptedSlap.prepare(sender_id, sender_key, post) + data = DiasporaFederation::Salmon::EncryptedSlap.prepare(post.author, sender_key, post) .generate_xml(recipient_key) expect(DiasporaFederation.callbacks).to receive(:trigger).with( @@ -90,7 +89,7 @@ module DiasporaFederation end it "raises when recipient private key is not available" do - magic_env = Salmon::MagicEnvelope.new(post, sender_id).envelop(sender_key) + magic_env = Salmon::MagicEnvelope.new(post, post.author).envelop(sender_key) data = Salmon::EncryptedMagicEnvelope.encrypt(magic_env, recipient_key.public_key) expect {