Skip to content
Extraits de code Groupes Projets
Valider 922d26f9 rédigé par cmrd Senya's avatar cmrd Senya Validation de Jonne Haß
Parcourir les fichiers

Implement integration tests for the federation messages receive feature

These are some initial tests, more to come.

It tests some features of Request, StatusMessage, Comment, Like,
Participation, Retraction, SignedRetraction, RelayableRetraction entities
receive process.
parent 08b910bd
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
...@@ -317,6 +317,36 @@ class Person < ActiveRecord::Base ...@@ -317,6 +317,36 @@ class Person < ActiveRecord::Base
self self
end end
def webfinger
DiasporaFederation::Discovery::WebFinger.new(
acct_uri: "acct:#{diaspora_handle}",
alias_url: AppConfig.url_to("/people/#{guid}"),
hcard_url: AppConfig.url_to(DiasporaFederation::Engine.routes.url_helpers.hcard_path(guid)),
seed_url: AppConfig.pod_uri,
profile_url: profile_url,
atom_url: atom_url,
salmon_url: receive_url,
guid: guid,
public_key: serialized_public_key
)
end
def hcard
DiasporaFederation::Discovery::HCard.new(
guid: guid,
nickname: username,
full_name: "#{profile.first_name} #{profile.last_name}".strip,
url: AppConfig.pod_uri,
photo_large_url: image_url,
photo_medium_url: image_url(:thumb_medium),
photo_small_url: image_url(:thumb_small),
public_key: serialized_public_key,
searchable: searchable,
first_name: profile.first_name,
last_name: profile.last_name
)
end
protected protected
def clean_url def clean_url
......
...@@ -8,38 +8,12 @@ DiasporaFederation.configure do |config| ...@@ -8,38 +8,12 @@ DiasporaFederation.configure do |config|
config.define_callbacks do config.define_callbacks do
on :fetch_person_for_webfinger do |handle| on :fetch_person_for_webfinger do |handle|
person = Person.find_local_by_diaspora_handle(handle) person = Person.find_local_by_diaspora_handle(handle)
if person person.webfinger if person
DiasporaFederation::Discovery::WebFinger.new(
acct_uri: "acct:#{person.diaspora_handle}",
alias_url: AppConfig.url_to("/people/#{person.guid}"),
hcard_url: AppConfig.url_to(DiasporaFederation::Engine.routes.url_helpers.hcard_path(person.guid)),
seed_url: AppConfig.pod_uri,
profile_url: person.profile_url,
atom_url: person.atom_url,
salmon_url: person.receive_url,
guid: person.guid,
public_key: person.serialized_public_key
)
end
end end
on :fetch_person_for_hcard do |guid| on :fetch_person_for_hcard do |guid|
person = Person.find_local_by_guid(guid) person = Person.find_local_by_guid(guid)
if person person.hcard if person
DiasporaFederation::Discovery::HCard.new(
guid: person.guid,
nickname: person.username,
full_name: "#{person.profile.first_name} #{person.profile.last_name}".strip,
url: AppConfig.pod_uri,
photo_large_url: person.image_url,
photo_medium_url: person.image_url(:thumb_medium),
photo_small_url: person.image_url(:thumb_small),
public_key: person.serialized_public_key,
searchable: person.searchable,
first_name: person.profile.first_name,
last_name: person.profile.last_name
)
end
end end
on :save_person_after_webfinger do |person| on :save_person_after_webfinger do |person|
...@@ -61,5 +35,33 @@ DiasporaFederation.configure do |config| ...@@ -61,5 +35,33 @@ DiasporaFederation.configure do |config|
person_entity.save! person_entity.save!
end end
on :fetch_private_key_by_diaspora_id do |diaspora_id|
key = Person.where(diaspora_handle: diaspora_id).joins(:owner).pluck(:serialized_private_key).first
OpenSSL::PKey::RSA.new key unless key.nil?
end
on :fetch_author_private_key_by_entity_guid do |entity_type, guid|
key = entity_type.constantize.where(guid: guid).joins(author: :owner).pluck(:serialized_private_key).first
OpenSSL::PKey::RSA.new key unless key.nil?
end
on :fetch_public_key_by_diaspora_id do |diaspora_id|
key = Person.where(diaspora_handle: diaspora_id).pluck(:serialized_public_key).first
OpenSSL::PKey::RSA.new key unless key.nil?
end
on :fetch_author_public_key_by_entity_guid do |entity_type, guid|
key = entity_type.constantize.where(guid: guid).joins(:author).pluck(:serialized_public_key).first
OpenSSL::PKey::RSA.new key unless key.nil?
end
on :entity_author_is_local? do |entity_type, guid|
entity_type.constantize.where(guid: guid).joins(author: :owner).exists?
end
on :fetch_entity_author_id_by_guid do |entity_type, guid|
entity_type.constantize.where(guid: guid).joins(:author).pluck(:diaspora_handle).first
end
end end
end end
require "spec_helper" require "spec_helper"
require "diaspora_federation/test"
describe "diaspora federation callbacks" do describe "diaspora federation callbacks" do
describe ":fetch_person_for_webfinger" do describe ":fetch_person_for_webfinger" do
...@@ -147,4 +148,122 @@ describe "diaspora federation callbacks" do ...@@ -147,4 +148,122 @@ describe "diaspora federation callbacks" do
end end
end end
end end
def create_a_local_person
FactoryGirl.create(:user).person
end
def create_a_remote_person
FactoryGirl.create(:person)
end
def create_post_by_a_local_person
FactoryGirl.create(:status_message, author: create_a_local_person).guid
end
def create_post_by_a_remote_person
FactoryGirl.create(:status_message, author: create_a_remote_person).guid
end
describe :fetch_private_key_by_diaspora_id do
it "returns a private key for a local user" do
expect(
DiasporaFederation.callbacks.trigger(described_class, create_a_local_person.diaspora_handle)
).not_to be_nil
end
it "returns nil for a remote user" do
expect(
DiasporaFederation.callbacks.trigger(described_class, create_a_remote_person.diaspora_handle)
).to be_nil
end
it "returns nil for an unknown id" do
expect(
DiasporaFederation.callbacks.trigger(described_class, FactoryGirl.generate(:diaspora_id))
).to be_nil
end
end
describe :fetch_author_private_key_by_entity_guid do
it "returns a private key for a post by a local user" do
expect(
DiasporaFederation.callbacks.trigger(described_class, "Post", create_post_by_a_local_person)
).not_to be_nil
end
it "returns nil for a post by a remote user" do
expect(
DiasporaFederation.callbacks.trigger(described_class, "Post", create_post_by_a_remote_person)
).to be_nil
end
it "returns nil for an unknown post" do
expect(
DiasporaFederation.callbacks.trigger(described_class, "Post", FactoryGirl.generate(:guid))
).to be_nil
end
end
describe :fetch_public_key_by_diaspora_id do
it "returns a public key for a person" do
expect(
DiasporaFederation.callbacks.trigger(described_class, create_a_remote_person.diaspora_handle)
).not_to be_nil
end
it "returns nil for an unknown person" do
expect(
DiasporaFederation.callbacks.trigger(described_class, FactoryGirl.generate(:diaspora_id))
).to be_nil
end
end
describe :fetch_author_public_key_by_entity_guid do
it "returns a public key for a known post" do
expect(
DiasporaFederation.callbacks.trigger(described_class, "Post", create_post_by_a_remote_person)
).not_to be_nil
end
it "returns nil for an unknown post" do
expect(
DiasporaFederation.callbacks.trigger(described_class, "Post", FactoryGirl.generate(:guid))
).to be_nil
end
end
describe :entity_author_is_local? do
it "returns true for a post by a local user" do
expect(
DiasporaFederation.callbacks.trigger(described_class, "Post", create_post_by_a_local_person)
).to be(true)
end
it "returns false for a post by a remote user" do
expect(
DiasporaFederation.callbacks.trigger(described_class, "Post", create_post_by_a_remote_person)
).to be(false)
end
it "returns false for a unknown post" do
expect(
DiasporaFederation.callbacks.trigger(described_class, "Post", FactoryGirl.generate(:diaspora_id))
).to be(false)
end
end
describe :fetch_entity_author_id_by_guid do
it "returns id for a existing guid" do
expect(
DiasporaFederation.callbacks.trigger(described_class, "Post", create_post_by_a_remote_person)
).not_to be_nil
end
it "returns nil for a non-existing guid" do
expect(
DiasporaFederation.callbacks.trigger(described_class, "Post", FactoryGirl.generate(:guid))
).to be_nil
end
end
end end
def generate_xml(entity, remote_user, user)
DiasporaFederation::Salmon::EncryptedSlap.generate_xml(
remote_user.diaspora_handle,
OpenSSL::PKey::RSA.new(remote_user.encryption_key),
entity,
OpenSSL::PKey::RSA.new(user.encryption_key)
)
end
def generate_status_message
@entity = FactoryGirl.build(
:status_message_entity,
diaspora_id: @remote_user.diaspora_handle,
public: false
)
generate_xml(@entity, @remote_user, @user)
end
def generate_forged_status_message
substitute_wrong_key(@remote_user, 1)
generate_status_message
end
def mock_private_key_for_user(user)
expect(DiasporaFederation.callbacks).to receive(:trigger)
.with(:fetch_private_key_by_diaspora_id, user.person.diaspora_handle)
.once
.and_return(user.encryption_key)
end
def retraction_mock_callbacks(entity, sender)
return unless [
DiasporaFederation::Entities::SignedRetraction,
DiasporaFederation::Entities::RelayableRetraction
].include?(entity.class)
mock_private_key_for_user(sender)
allow(DiasporaFederation.callbacks).to receive(:trigger)
.with(
:fetch_entity_author_id_by_guid,
entity.target_type,
entity.target_guid
)
.once
.and_return(sender.encryption_key)
end
def generate_retraction(entity_name, target_object, sender=@remote_user)
@entity = FactoryGirl.build(
entity_name,
diaspora_id: sender.diaspora_handle,
target_guid: target_object.guid,
target_type: target_object.class.to_s
)
retraction_mock_callbacks(@entity, sender)
generate_xml(@entity, sender, @user)
end
def generate_forged_retraction(entity_name, target_object, sender=@remote_user)
times = 1
if %i(signed_retraction_entity relayable_retraction_entity).include?(entity_name)
times += 2
end
substitute_wrong_key(sender, times)
generate_retraction(entity_name, target_object, sender)
end
def generate_relayable_local_parent(entity_name)
@entity = FactoryGirl.build(
entity_name,
parent_guid: @local_message.guid,
diaspora_id: @remote_user.person.diaspora_handle
)
mock_private_key_for_user(@remote_user)
expect(DiasporaFederation.callbacks).to receive(:trigger)
.with(:fetch_author_private_key_by_entity_guid, "Post", kind_of(String))
.and_return(nil)
generate_xml(@entity, @remote_user, @user)
end
def generate_relayable_remote_parent(entity_name)
@entity = FactoryGirl.build(
entity_name,
parent_guid: @remote_message.guid,
diaspora_id: @remote_user2.person.diaspora_handle
)
mock_private_key_for_user(@remote_user2)
expect(DiasporaFederation.callbacks).to receive(:trigger)
.with(
:fetch_author_private_key_by_entity_guid,
"Post",
@remote_message.guid
)
.once
.and_return(@remote_user.encryption_key)
generate_xml(@entity, @remote_user, @user)
end
def substitute_wrong_key(user, times_number)
expect(user).to receive(:encryption_key).exactly(times_number).times.and_return(
OpenSSL::PKey::RSA.new(1024)
)
end
# Checks when a remote pod wants to send us a relayable without having a key for declared diaspora ID
def generate_relayable_local_parent_wrong_author_key(entity_name)
substitute_wrong_key(@remote_user, 2)
generate_relayable_local_parent(entity_name)
end
# Checks when a remote pod C wants to send us a relayable from its user, but bypassing the pod B where
# remote status came from.
def generate_relayable_remote_parent_wrong_parent_key(entity_name)
substitute_wrong_key(@remote_user, 2)
generate_relayable_remote_parent(entity_name)
end
require "spec_helper"
require "diaspora_federation/test"
require "integration/federation/federation_messages_generation"
require "integration/federation/shared_receive_relayable"
require "integration/federation/shared_receive_retraction"
describe Workers::ReceiveEncryptedSalmon do
before do
@user = alice
allow(User).to receive(:find) { |id|
@user if id == @user.id
}
@remote_user = FactoryGirl.build(:user) # user on pod B
@remote_user2 = FactoryGirl.build(:user) # user on pod C
allow_any_instance_of(DiasporaFederation::Discovery::Discovery)
.to receive(:webfinger) {|instance|
[@remote_user, @remote_user2].find {|user| user.diaspora_handle == instance.diaspora_id }.person.webfinger
}
allow_any_instance_of(DiasporaFederation::Discovery::Discovery)
.to receive(:hcard) {|instance|
[@remote_user, @remote_user2].find {|user| user.diaspora_handle == instance.diaspora_id }.person.hcard
}
@remote_person = Person.find_or_fetch_by_identifier(@remote_user.diaspora_handle)
@remote_person2 = Person.find_or_fetch_by_identifier(@remote_user2.diaspora_handle)
end
it "treats sharing request recive correctly" do
entity = FactoryGirl.build(:request_entity, recipient_id: @user.diaspora_handle)
expect(Diaspora::Fetcher::Public).to receive(:queue_for).exactly(1).times
Workers::ReceiveEncryptedSalmon.new.perform(@user.id, generate_xml(entity, @remote_user, @user))
expect(@user.contacts.count).to eq(2)
new_contact = @user.contacts.order(created_at: :asc).last
expect(new_contact).not_to be_nil
expect(new_contact.sharing).to eq(true)
expect(new_contact.person.diaspora_handle).to eq(@remote_user.diaspora_handle)
end
it "doesn't save the status message if there is no sharing" do
Workers::ReceiveEncryptedSalmon.new.perform(@user.id, generate_status_message)
expect(StatusMessage.exists?(guid: @entity.guid)).to be(false)
end
describe "with messages which require sharing" do
before do
@remote_person = Person.find_or_fetch_by_identifier(@remote_user.diaspora_handle)
contact = @user.contacts.find_or_initialize_by(person_id: @remote_person.id)
contact.sharing = true
contact.save
end
it "treats status message receive correctly" do
Workers::ReceiveEncryptedSalmon.new.perform(@user.id, generate_status_message)
expect(StatusMessage.exists?(guid: @entity.guid)).to be(true)
end
it "doesn't accept status message with wrong signature" do
Workers::ReceiveEncryptedSalmon.new.perform(@user.id, generate_forged_status_message)
expect(StatusMessage.exists?(guid: @entity.guid)).to be(false)
end
describe "retractions for non-relayable objects" do
%w(
retraction
signed_retraction
).each do |retraction_entity_name|
context "with #{retraction_entity_name}" do
%w(status_message photo).each do |target|
context "with #{target}" do
it_behaves_like "it retracts non-relayable object" do
let(:target_object) { FactoryGirl.create(target.to_sym, author: @remote_person) }
let(:entity_name) { "#{retraction_entity_name}_entity".to_sym }
end
end
end
end
end
end
describe "with messages which require a status to operate on" do
before do
@local_message = FactoryGirl.create(:status_message, author: @user.person)
@remote_message = FactoryGirl.create(:status_message, author: @remote_person)
end
%w(comment like participation).each do |entity|
context "with #{entity}" do
it_behaves_like "it deals correctly with a relayable" do
let(:entity_name) { "#{entity}_entity".to_sym }
let(:klass) { entity.camelize.constantize }
end
end
end
describe "retractions for relayable objects" do
%w(
retraction
signed_retraction
relayable_retraction
).each do |retraction_entity_name|
context "with #{retraction_entity_name}" do
context "with comment" do
it_behaves_like "it retracts relayable object" do
# case for to-upstream federation
let(:entity_name) { "#{retraction_entity_name}_entity".to_sym }
let(:target_object) { FactoryGirl.create(:comment, author: @remote_person, post: @local_message) }
let(:sender) { @remote_user }
end
it_behaves_like "it retracts relayable object" do
# case for to-downsteam federation
let(:target_object) { FactoryGirl.create(:comment, author: @remote_person2, post: @remote_message) }
let(:entity_name) { "#{retraction_entity_name}_entity".to_sym }
let(:sender) { @remote_user }
end
end
context "with like" do
it_behaves_like "it retracts relayable object" do
# case for to-upstream federation
let(:entity_name) { "#{retraction_entity_name}_entity".to_sym }
let(:target_object) { FactoryGirl.create(:like, author: @remote_person, target: @local_message) }
let(:sender) { @remote_user }
end
it_behaves_like "it retracts relayable object" do
# case for to-downsteam federation
let(:target_object) { FactoryGirl.create(:like, author: @remote_person2, target: @remote_message) }
let(:entity_name) { "#{retraction_entity_name}_entity".to_sym }
let(:sender) { @remote_user }
end
end
end
end
end
end
end
end
shared_examples_for "it deals correctly with a relayable" do
it "treats upstream receive correctly" do
Workers::ReceiveEncryptedSalmon.new.perform(@user.id, generate_relayable_local_parent(entity_name))
received_entity = klass.find_by(guid: @entity.guid)
expect(received_entity).not_to be_nil
expect(received_entity.author.diaspora_handle).to eq(@remote_person.diaspora_handle)
end
it "rejects an upstream entity with a malformed author signature" do
Workers::ReceiveEncryptedSalmon.new.perform(
@user.id,
generate_relayable_local_parent_wrong_author_key(entity_name)
)
expect(klass.exists?(guid: @entity.guid)).to be(false)
end
it "treats downstream receive correctly" do
Workers::ReceiveEncryptedSalmon.new.perform(@user.id, generate_relayable_remote_parent(entity_name))
received_entity = klass.find_by(guid: @entity.guid)
expect(received_entity).not_to be_nil
expect(received_entity.author.diaspora_handle).to eq(@remote_person2.diaspora_handle)
end
it "declines downstream receive when sender signed with a wrong key" do
Workers::ReceiveEncryptedSalmon.new.perform(
@user.id,
generate_relayable_remote_parent_wrong_parent_key(entity_name)
)
expect(klass.exists?(guid: @entity.guid)).to be(false)
end
end
shared_examples_for "it retracts non-relayable object" do
it "retracts object by a correct retraction message" do
target_klass = target_object.class.to_s.constantize
Workers::ReceiveEncryptedSalmon.new.perform(@user.id, generate_retraction(entity_name, target_object))
expect(target_klass.exists?(guid: target_object.guid)).to be(false)
end
it "doesn't retract object when retraction has wrong signatures" do
target_klass = target_object.class.to_s.constantize
Workers::ReceiveEncryptedSalmon.new.perform(@user.id, generate_forged_retraction(entity_name, target_object))
expect(target_klass.exists?(guid: target_object.guid)).to be(true)
end
it "doesn't retract object when sender is different from target object" do
target_klass = target_object.class.to_s.constantize
Workers::ReceiveEncryptedSalmon.new.perform(
@user.id,
generate_retraction(entity_name, target_object, @remote_user2)
)
expect(target_klass.exists?(guid: target_object.guid)).to be(true)
end
end
shared_examples_for "it retracts relayable object" do
it "retracts object by a correct message" do
target_klass = target_object.class.to_s.constantize
Workers::ReceiveEncryptedSalmon.new.perform(@user.id, generate_retraction(entity_name, target_object, sender))
expect(target_klass.exists?(guid: target_object.guid)).to be(false)
end
it "doesn't retract object when retraction has wrong signatures" do
target_klass = target_object.class.to_s.constantize
Workers::ReceiveEncryptedSalmon.new.perform(
@user.id,
generate_forged_retraction(entity_name, target_object, sender)
)
expect(target_klass.exists?(guid: target_object.guid)).to be(true)
end
end
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter