Receiving entities support

This commit is contained in:
cmrd Senya 2015-12-13 22:39:59 +03:00
parent 6615e67d80
commit 5aac8c2423
11 changed files with 196 additions and 6 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,3 +1,5 @@
require "json"
module DiasporaFederation
module Salmon
# +EncryptedSlap+ provides class methods for generating and parsing encrypted

View file

@ -1,3 +1,4 @@
require "diaspora_federation"
require "factory_girl"
FactoryGirl.define do

View file

@ -3,27 +3,44 @@ module DiasporaFederation
routes { DiasporaFederation::Engine.routes }
describe "POST #public" do
it "succeeds" do
post :public, xml: "<diaspora/>"
expect(response).to be_success
it "raises on an empty object" do
expect { post :public, xml: "<diaspora/>" }.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: "<diaspora/>"
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: "<diaspora/>"
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: "<diaspora/>"
expect(response.code).to eq("200")
end
end
end
end

View file

@ -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, "<XML/>").receive! }.to raise_error Salmon::MissingHeader
end
end
end

View file

@ -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("<XML/>").receive! }.to raise_error Salmon::MissingAuthor
end
end
end

View file

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