diff --git a/app/controllers/diaspora_federation/receive_controller.rb b/app/controllers/diaspora_federation/receive_controller.rb
index 733cf4d..4a41ad2 100644
--- a/app/controllers/diaspora_federation/receive_controller.rb
+++ b/app/controllers/diaspora_federation/receive_controller.rb
@@ -1,4 +1,5 @@
require_dependency "diaspora_federation/application_controller"
+require "diaspora_federation/receiver"
module DiasporaFederation
# this controller processes receiving messages
@@ -11,6 +12,7 @@ module DiasporaFederation
def public
logger.info "received a public message"
logger.debug CGI.unescape(params[:xml])
+ Receiver::Public.new(CGI.unescape(params[:xml])).receive!
render nothing: true, status: :ok
end
@@ -20,7 +22,12 @@ module DiasporaFederation
def private
logger.info "received a private message for #{params[:guid]}"
logger.debug CGI.unescape(params[:xml])
- render nothing: true, status: :ok
+ begin
+ Receiver::Private.new(params[:guid], CGI.unescape(params[:xml])).receive!
+ render nothing: true, status: :ok
+ rescue RecipientNotFound
+ render nothing: true, status: 404
+ end
end
private
diff --git a/lib/diaspora_federation.rb b/lib/diaspora_federation.rb
index 3390be0..73f9204 100644
--- a/lib/diaspora_federation.rb
+++ b/lib/diaspora_federation.rb
@@ -12,6 +12,7 @@ require "diaspora_federation/entities"
require "diaspora_federation/discovery"
require "diaspora_federation/salmon"
+require "diaspora_federation/receiver"
# diaspora* federation library
module DiasporaFederation
@@ -22,11 +23,13 @@ module DiasporaFederation
fetch_person_for_hcard
save_person_after_webfinger
fetch_private_key_by_diaspora_id
+ fetch_private_key_by_user_guid
fetch_author_private_key_by_entity_guid
fetch_public_key_by_diaspora_id
fetch_author_public_key_by_entity_guid
entity_author_is_local?
fetch_entity_author_id_by_guid
+ entity_persist
)
class << self
diff --git a/lib/diaspora_federation/receiver.rb b/lib/diaspora_federation/receiver.rb
new file mode 100644
index 0000000..eca48eb
--- /dev/null
+++ b/lib/diaspora_federation/receiver.rb
@@ -0,0 +1,24 @@
+module DiasporaFederation
+ # SenderNotFound is raised when failed to fetch a public key of the sender of the received message
+ class SenderNotFound < Exception
+ end
+
+ # Common base for Private and Public receivers
+ # @see Receiver::Public
+ # @see Receiver::Private
+ class Receiver
+ def initialize(salmon_xml)
+ @salmon_xml = salmon_xml
+ end
+
+ def receive!
+ sender_id = slap.author_id
+ pkey = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, sender_id)
+ raise SenderNotFound if pkey.nil?
+ DiasporaFederation.callbacks.trigger(:entity_persist, slap.entity(pkey), @recipient_guid, sender_id)
+ end
+ end
+end
+
+require "diaspora_federation/receiver/private"
+require "diaspora_federation/receiver/public"
diff --git a/lib/diaspora_federation/receiver/private.rb b/lib/diaspora_federation/receiver/private.rb
new file mode 100644
index 0000000..789355d
--- /dev/null
+++ b/lib/diaspora_federation/receiver/private.rb
@@ -0,0 +1,24 @@
+module DiasporaFederation
+ # RecipientNotFound is raised when failed to fetch a private key of the recipient of the received message
+ class RecipientNotFound < Exception
+ end
+
+ class Receiver
+ # Receiver::Private is used to receive private messages, which are addressed to a specific user, encrypted with his
+ # public key and packed using Salmon::EncryptedSlap
+ class Private < Receiver
+ def initialize(recipient_guid, salmon_xml)
+ super(salmon_xml)
+ @recipient_guid = recipient_guid
+ @recipient_private_key = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_user_guid, recipient_guid)
+ raise RecipientNotFound if @recipient_private_key.nil?
+ end
+
+ protected
+
+ def slap
+ @salmon ||= Salmon::EncryptedSlap.from_xml(@salmon_xml, @recipient_private_key)
+ end
+ end
+ end
+end
diff --git a/lib/diaspora_federation/receiver/public.rb b/lib/diaspora_federation/receiver/public.rb
new file mode 100644
index 0000000..8ee22fa
--- /dev/null
+++ b/lib/diaspora_federation/receiver/public.rb
@@ -0,0 +1,13 @@
+module DiasporaFederation
+ class Receiver
+ # Receiver::Public is used to receive public messages, which are not addressed to a specific user, unencrypted
+ # and packed using Salmon::Slap
+ class Public < Receiver
+ protected
+
+ def slap
+ @salmon ||= Salmon::Slap.from_xml(@salmon_xml)
+ end
+ end
+ end
+end
diff --git a/lib/diaspora_federation/salmon/encrypted_slap.rb b/lib/diaspora_federation/salmon/encrypted_slap.rb
index 10cd22e..da45e7f 100644
--- a/lib/diaspora_federation/salmon/encrypted_slap.rb
+++ b/lib/diaspora_federation/salmon/encrypted_slap.rb
@@ -1,3 +1,5 @@
+require "json"
+
module DiasporaFederation
module Salmon
# +EncryptedSlap+ provides class methods for generating and parsing encrypted
diff --git a/lib/diaspora_federation/test/factories.rb b/lib/diaspora_federation/test/factories.rb
index c54ee50..e332e5f 100644
--- a/lib/diaspora_federation/test/factories.rb
+++ b/lib/diaspora_federation/test/factories.rb
@@ -1,3 +1,4 @@
+require "diaspora_federation"
require "factory_girl"
FactoryGirl.define do
diff --git a/spec/controllers/diaspora_federation/receive_controller_spec.rb b/spec/controllers/diaspora_federation/receive_controller_spec.rb
index 2edc051..d1de84b 100644
--- a/spec/controllers/diaspora_federation/receive_controller_spec.rb
+++ b/spec/controllers/diaspora_federation/receive_controller_spec.rb
@@ -3,27 +3,44 @@ module DiasporaFederation
routes { DiasporaFederation::Engine.routes }
describe "POST #public" do
- it "succeeds" do
- post :public, xml: ""
- expect(response).to be_success
+ it "raises on an empty object" do
+ expect { post :public, xml: "" }.to raise_error(Salmon::MissingAuthor)
end
it "returns a 422 if no xml is passed" do
post :public
expect(response.code).to eq("422")
end
+
+ it "returns a 200 if receive! reports no errors" do
+ expect_any_instance_of(Receiver::Public).to receive(:receive!)
+
+ post :public, xml: ""
+ expect(response.code).to eq("200")
+ end
end
describe "POST #private" do
- it "succeeds" do
+ it "return 404 for because of an unknown guid" do
post :private, guid: "any-guid", xml: ""
- expect(response).to be_success
+ expect(response.code).to eq("404")
end
it "returns a 422 if no xml is passed" do
post :private, guid: "any-guid"
expect(response.code).to eq("422")
end
+
+ it "returns a 200 if receive! reports no errors" do
+ expect(DiasporaFederation.callbacks).to receive(:trigger)
+ .with(:fetch_private_key_by_user_guid, "any-guid")
+ .once
+ .and_return(true)
+ expect_any_instance_of(Receiver::Private).to receive(:receive!)
+
+ post :private, guid: "any-guid", xml: ""
+ expect(response.code).to eq("200")
+ end
end
end
end
diff --git a/spec/lib/diaspora_federation/receiver/private_spec.rb b/spec/lib/diaspora_federation/receiver/private_spec.rb
new file mode 100644
index 0000000..ae14252
--- /dev/null
+++ b/spec/lib/diaspora_federation/receiver/private_spec.rb
@@ -0,0 +1,57 @@
+module DiasporaFederation
+ describe Receiver::Private 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(:recipient_guid) { FactoryGirl.generate(:guid) }
+ let(:xml) {
+ DiasporaFederation::Salmon::EncryptedSlap.generate_xml(
+ sender_id,
+ sender_key,
+ FactoryGirl.build(:request_entity),
+ recipient_key
+ )
+ }
+
+ it "calls entity_persist if everyting 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(:fetch_private_key_by_user_guid, recipient_guid)
+ .and_return(recipient_key)
+
+ expect(DiasporaFederation.callbacks).to receive(:trigger)
+ .with(:entity_persist, kind_of(Entity), recipient_guid, sender_id)
+
+ described_class.new(recipient_guid, xml).receive!
+ end
+
+ it "raises when sender public key is not available" do
+ expect(DiasporaFederation.callbacks).to receive(:trigger)
+ .with(:fetch_public_key_by_diaspora_id, sender_id)
+ .and_return(nil)
+ expect(DiasporaFederation.callbacks).to receive(:trigger)
+ .with(:fetch_private_key_by_user_guid, recipient_guid)
+ .and_return(recipient_key)
+
+ expect { described_class.new(recipient_guid, xml).receive! }.to raise_error SenderNotFound
+ end
+
+ it "raises when recipient private key is not available" do
+ expect(DiasporaFederation.callbacks).to receive(:trigger)
+ .with(:fetch_private_key_by_user_guid, recipient_guid)
+ .and_return(nil)
+
+ expect { described_class.new(recipient_guid, xml).receive! }.to raise_error RecipientNotFound
+ end
+
+ it "raises when bad xml was supplied" do
+ expect(DiasporaFederation.callbacks).to receive(:trigger)
+ .with(:fetch_private_key_by_user_guid, recipient_guid)
+ .and_return(recipient_key)
+
+ expect { described_class.new(recipient_guid, "").receive! }.to raise_error Salmon::MissingHeader
+ end
+ end
+end
diff --git a/spec/lib/diaspora_federation/receiver/public_spec.rb b/spec/lib/diaspora_federation/receiver/public_spec.rb
new file mode 100644
index 0000000..13eb82e
--- /dev/null
+++ b/spec/lib/diaspora_federation/receiver/public_spec.rb
@@ -0,0 +1,35 @@
+module DiasporaFederation
+ describe Receiver::Public 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)
+ )
+ }
+
+ it "calls entity_persist if everyting 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(:entity_persist, kind_of(Entity), nil, sender_id)
+
+ described_class.new(xml).receive!
+ end
+
+ it "raises when sender public key is not available" do
+ expect(DiasporaFederation.callbacks).to receive(:trigger)
+ .with(:fetch_public_key_by_diaspora_id, sender_id)
+ .and_return(nil)
+
+ expect { described_class.new(xml).receive! }.to raise_error SenderNotFound
+ end
+
+ it "raises when bad xml was supplied" do
+ expect { described_class.new("").receive! }.to raise_error Salmon::MissingAuthor
+ end
+ end
+end
diff --git a/test/dummy/config/initializers/diaspora_federation.rb b/test/dummy/config/initializers/diaspora_federation.rb
index 95a8ed4..39b315d 100644
--- a/test/dummy/config/initializers/diaspora_federation.rb
+++ b/test/dummy/config/initializers/diaspora_federation.rb
@@ -86,5 +86,12 @@ DiasporaFederation.configure do |config|
on :fetch_entity_author_id_by_guid do
nil
end
+
+ on :fetch_private_key_by_user_guid do
+ nil
+ end
+
+ on :entity_persist do
+ end
end
end