create receiver for new protocol and write tests

This commit is contained in:
Benjamin Neff 2016-03-17 19:03:33 +01:00
parent 198e23ca65
commit bd9cc67f5e
14 changed files with 255 additions and 37 deletions

View file

@ -29,7 +29,7 @@ module DiasporaFederation
fetch_entity_author_id_by_guid fetch_entity_author_id_by_guid
queue_public_receive queue_public_receive
queue_private_receive queue_private_receive
save_entity_after_receive receive_entity
fetch_public_entity fetch_public_entity
fetch_person_url_to fetch_person_url_to
update_pod 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 # @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 # @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 # 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 [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_public_entity
# fetch a public entity from the database # fetch a public entity from the database

View file

@ -13,7 +13,7 @@ module DiasporaFederation
magic_env = Nokogiri::XML::Document.parse(response.body).root magic_env = Nokogiri::XML::Document.parse(response.body).root
entity = Salmon::MagicEnvelope.unenvelop(magic_env) entity = Salmon::MagicEnvelope.unenvelop(magic_env)
DiasporaFederation.callbacks.trigger(:save_entity_after_receive, entity) DiasporaFederation.callbacks.trigger(:receive_entity, entity)
rescue => e rescue => e
raise NotFetchable, "Failed to fetch #{entity_type}:#{guid} from #{author}: #{e.class}: #{e.message}" raise NotFetchable, "Failed to fetch #{entity_type}:#{guid} from #{author}: #{e.class}: #{e.message}"
end end

View file

@ -1,6 +1,36 @@
module DiasporaFederation module DiasporaFederation
module Federation module Federation
# this module is for parse and receive entities.
module Receiver 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 end
end end
@ -8,3 +38,5 @@ end
require "diaspora_federation/federation/receiver/slap_receiver" require "diaspora_federation/federation/receiver/slap_receiver"
require "diaspora_federation/federation/receiver/private_slap_receiver" require "diaspora_federation/federation/receiver/private_slap_receiver"
require "diaspora_federation/federation/receiver/public_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"

View file

@ -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

View file

@ -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

View file

@ -10,7 +10,6 @@ module DiasporaFederation
# @param [OpenSSL::PKey::RSA] recipient_private_key recipient private key to decrypt the message # @param [OpenSSL::PKey::RSA] recipient_private_key recipient private key to decrypt the message
def initialize(slap_xml, recipient_private_key) def initialize(slap_xml, recipient_private_key)
super(slap_xml) super(slap_xml)
raise ArgumentError, "no recipient key provided" unless recipient_private_key.instance_of?(OpenSSL::PKey::RSA)
@recipient_private_key = recipient_private_key @recipient_private_key = recipient_private_key
end end

View file

@ -12,12 +12,12 @@ module DiasporaFederation
@slap_xml = slap_xml @slap_xml = slap_xml
end end
# Parse the salmon xml and send it to the +:save_entity_after_receive+ callback # Parse the salmon xml
def receive! def parse
sender_id = slap.author_id sender_id = slap.author_id
public_key = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, sender_id) public_key = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, sender_id)
raise Salmon::SenderKeyNotFound if public_key.nil? 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 end
end end

View file

@ -15,7 +15,7 @@ module DiasporaFederation
:fetch_public_key_by_diaspora_id, post.author :fetch_public_key_by_diaspora_id, post.author
).and_return(alice.public_key) ).and_return(alice.public_key)
expect(DiasporaFederation.callbacks).to receive(:trigger).with( expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:save_entity_after_receive, kind_of(Entities::StatusMessage) :receive_entity, kind_of(Entities::StatusMessage)
) do |_, entity| ) do |_, entity|
expect(entity.guid).to eq(post.guid) expect(entity.guid).to eq(post.guid)
expect(entity.author).to eq(post.author) expect(entity.author).to eq(post.author)
@ -39,7 +39,7 @@ module DiasporaFederation
:fetch_public_key_by_diaspora_id, post.author :fetch_public_key_by_diaspora_id, post.author
).and_return(alice.public_key) ).and_return(alice.public_key)
expect(DiasporaFederation.callbacks).to receive(:trigger).with( 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) Federation::Fetcher.fetch_public(post.author, :post, post.guid)

View file

@ -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

View file

@ -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

View file

@ -3,19 +3,20 @@ module DiasporaFederation
let(:sender_id) { FactoryGirl.generate(:diaspora_id) } let(:sender_id) { FactoryGirl.generate(:diaspora_id) }
let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) } let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) }
let(:recipient_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) { let(:xml) {
DiasporaFederation::Salmon::EncryptedSlap.prepare(sender_id, sender_key, FactoryGirl.build(:request_entity)) DiasporaFederation::Salmon::EncryptedSlap.prepare(sender_id, sender_key, entity).generate_xml(recipient_key)
.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( expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_public_key_by_diaspora_id, sender_id :fetch_public_key_by_diaspora_id, sender_id
).and_return(sender_key) ).and_return(sender_key)
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:save_entity_after_receive, kind_of(Entity)) parsed_entity = described_class.new(xml, recipient_key).parse
expect(parsed_entity).to be_a(Entities::StatusMessage)
described_class.new(xml, recipient_key).receive! expect(parsed_entity.guid).to eq(entity.guid)
expect(parsed_entity.public).to eq("false")
end end
it "raises when sender public key is not available" do it "raises when sender public key is not available" do
@ -24,19 +25,13 @@ module DiasporaFederation
).and_return(nil) ).and_return(nil)
expect { expect {
described_class.new(xml, recipient_key).receive! described_class.new(xml, recipient_key).parse
}.to raise_error Salmon::SenderKeyNotFound }.to raise_error Salmon::SenderKeyNotFound
end 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 it "raises when bad xml was supplied" do
expect { expect {
described_class.new("<XML/>", recipient_key).receive! described_class.new("<XML/>", recipient_key).parse
}.to raise_error Salmon::MissingHeader }.to raise_error Salmon::MissingHeader
end end
end end

View file

@ -2,22 +2,18 @@ module DiasporaFederation
describe Federation::Receiver::PublicSlapReceiver do describe Federation::Receiver::PublicSlapReceiver do
let(:sender_id) { FactoryGirl.generate(:diaspora_id) } let(:sender_id) { FactoryGirl.generate(:diaspora_id) }
let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) } let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) }
let(:xml) { let(:entity) { FactoryGirl.build(:status_message_entity) }
DiasporaFederation::Salmon::Slap.generate_xml( let(:xml) { DiasporaFederation::Salmon::Slap.generate_xml(sender_id, sender_key, entity) }
sender_id,
sender_key,
FactoryGirl.build(:request_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( expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_public_key_by_diaspora_id, sender_id :fetch_public_key_by_diaspora_id, sender_id
).and_return(sender_key) ).and_return(sender_key)
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:save_entity_after_receive, kind_of(Entity)) parsed_entity = described_class.new(xml).parse
expect(parsed_entity).to be_a(Entities::StatusMessage)
described_class.new(xml).receive! expect(parsed_entity.guid).to eq(entity.guid)
expect(parsed_entity.public).to eq("true")
end end
it "raises when sender public key is not available" do it "raises when sender public key is not available" do
@ -26,13 +22,13 @@ module DiasporaFederation
).and_return(nil) ).and_return(nil)
expect { expect {
described_class.new(xml).receive! described_class.new(xml).parse
}.to raise_error Salmon::SenderKeyNotFound }.to raise_error Salmon::SenderKeyNotFound
end end
it "raises when bad xml was supplied" do it "raises when bad xml was supplied" do
expect { expect {
described_class.new("<XML/>").receive! described_class.new("<XML/>").parse
}.to raise_error Salmon::MissingAuthor }.to raise_error Salmon::MissingAuthor
end end
end end

View file

@ -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

View file

@ -97,7 +97,7 @@ DiasporaFederation.configure do |config|
true true
end end
on :save_entity_after_receive do on :receive_entity do
end end
on :fetch_public_entity do |entity_type, guid| on :fetch_public_entity do |entity_type, guid|