message not relayable anymore

receive only from author and ignore signature checking, or from parent
author with valid signature.

see #36
This commit is contained in:
Benjamin Neff 2016-06-16 22:18:17 +02:00
parent 7f869a5cdf
commit c62bede41c
6 changed files with 130 additions and 14 deletions

View file

@ -8,9 +8,6 @@ module DiasporaFederation
# @deprecated
LEGACY_SIGNATURE_ORDER = %i(guid parent_guid text created_at author conversation_guid).freeze
# The {Message} parent is a {Conversation}
PARENT_TYPE = "Conversation".freeze
include Relayable
# @!attribute [r] text
@ -28,6 +25,40 @@ module DiasporaFederation
# @see Conversation#guid
# @return [String] conversation guid
property :conversation_guid
# It is only valid to receive a {Message} from the author itself,
# or from the author of the parent {Conversation} if the author signature is valid.
# @deprecated remove after {Message} doesn't include {Relayable} anymore
def sender_valid?(sender)
sender == author || (sender == parent_author && verify_author_signature)
end
private
# @deprecated remove after {Message} doesn't include {Relayable} anymore
def verify_author_signature
pubkey = DiasporaFederation.callbacks.trigger(:fetch_public_key, author)
raise PublicKeyNotFound, "author_signature author=#{author} guid=#{guid}" if pubkey.nil?
raise SignatureVerificationFailed, "wrong author_signature" unless verify_signature(pubkey, author_signature)
true
end
# @deprecated remove after {Message} doesn't include {Relayable} anymore
def parent_author
parent = DiasporaFederation.callbacks.trigger(:fetch_related_entity, "Conversation", conversation_guid)
raise Federation::Fetcher::NotFetchable, "Conversation:#{conversation_guid} not found" unless parent
parent.author
end
# Default implementation, don't verify signatures for a {Message}.
# @see Entity.populate_entity
# @deprecated remove after {Message} doesn't include {Relayable} anymore
# @param [Nokogiri::XML::Element] root_node xml nodes
# @return [Entity] instance
def self.populate_entity(root_node)
new({parent_guid: nil, parent: nil}.merge(entity_data(root_node)))
end
private_class_method :populate_entity
end
end
end

View file

@ -4,8 +4,8 @@ module DiasporaFederation
class MessageValidator < Validation::Validator
include Validation
include RelayableValidator
rule :author, %i(not_empty diaspora_id)
rule :guid, :guid
rule :conversation_guid, :guid
end
end

View file

@ -35,7 +35,7 @@ XML
it_behaves_like "an Entity subclass"
it_behaves_like "an XML Entity"
it_behaves_like "an XML Entity", %i(parent parent_guid)
context "default values" do
let(:minimal_xml) {

View file

@ -25,8 +25,84 @@ XML
it_behaves_like "an Entity subclass"
it_behaves_like "an XML Entity"
it_behaves_like "an XML Entity", %i(parent parent_guid)
it_behaves_like "a relayable Entity"
describe "#sender_valid?" do
let(:entity) { Entities::Message.new(data) }
it "allows the author" do
expect(entity.sender_valid?(alice.diaspora_id)).to be_truthy
end
it "allows parent author if the signature is valid" do
expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(data[:parent])
expect_callback(:fetch_public_key, alice.diaspora_id).and_return(alice.private_key)
expect(entity.sender_valid?(bob.diaspora_id)).to be_truthy
end
it "does not allow any other person" do
expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(data[:parent])
invalid_sender = FactoryGirl.generate(:diaspora_id)
expect(entity.sender_valid?(invalid_sender)).to be_falsey
end
it "does not allow the parent author if the signature is invalid" do
expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(data[:parent])
expect_callback(:fetch_public_key, alice.diaspora_id).and_return(alice.private_key)
invalid_entity = Entities::Message.new(data.merge(author_signature: "aa"))
expect {
invalid_entity.sender_valid?(bob.diaspora_id)
}.to raise_error Entities::Relayable::SignatureVerificationFailed, "wrong author_signature"
end
it "raises NotFetchable if the parent Conversation can not be found" do
expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(nil)
expect {
entity.sender_valid?(bob.diaspora_id)
}.to raise_error Federation::Fetcher::NotFetchable
end
end
context "relayable signature verification" do
it "does not verify the signature" do
data.merge!(author_signature: "aa", parent_author_signature: "bb")
xml = Entities::Message.new(data).to_xml
expect {
Entities::Message.from_xml(xml)
}.not_to raise_error
end
end
describe ".populate_entity" do
it "adds a nil parent" do
xml = Entities::Message.new(data).to_xml
parsed = Entities::Message.from_xml(xml)
expect(parsed.parent).to be_nil
end
it "uses the parent_guid from the parsed xml" do
xml = Entities::Message.new(data).to_xml
parsed = Entities::Message.from_xml(xml)
expect(parsed.parent_guid).to eq(data[:parent_guid])
end
it "uses nil for parent_guid if not in the xml" do
xml = <<-XML
<message>
<author>#{data[:author]}</author>
<guid>#{data[:guid]}</guid>
<text>#{data[:text]}</text>
<created_at>#{data[:created_at]}</created_at>
<conversation_guid>#{data[:conversation_guid]}</conversation_guid>
</message>
XML
parsed = Entities::Message.from_xml(Nokogiri::XML::Document.parse(xml).root)
expect(parsed.parent_guid).to be_nil
end
end
end
end

View file

@ -3,7 +3,16 @@ module DiasporaFederation
let(:entity) { :message_entity }
it_behaves_like "a common validator"
it_behaves_like "a relayable validator"
it_behaves_like "a diaspora id validator" do
let(:property) { :author }
let(:mandatory) { true }
end
describe "#guid" do
it_behaves_like "a guid validator" do
let(:property) { :guid }
end
end
describe "#conversation_guid" do
it_behaves_like "a guid validator" do

View file

@ -66,21 +66,21 @@ shared_examples "an XML Entity" do |ignored_props=[]|
end
end
def check_entity(entity, parsed_entity, ignored_props=[])
entity.class.class_props.each do |name, type|
validate_values(entity.send(name), parsed_entity.send(name), type) unless ignored_props.include?(name)
def check_entity(entity, parsed_entity, ignored_props)
entity.class.class_props.reject {|name| ignored_props.include?(name) }.each do |name, type|
validate_values(entity.send(name), parsed_entity.send(name), type, ignored_props)
end
end
def validate_values(value, parsed_value, type)
def validate_values(value, parsed_value, type, ignored_props)
if value.nil?
expect(parsed_value).to be_nil
elsif type == String
expect(parsed_value.to_s).to eq(value.to_s)
elsif type.instance_of?(Array)
value.each_with_index {|entity, index| check_entity(entity, parsed_value[index]) }
value.each_with_index {|entity, index| check_entity(entity, parsed_value[index], ignored_props) }
elsif type.ancestors.include?(DiasporaFederation::Entity)
check_entity(value, parsed_value)
check_entity(value, parsed_value, ignored_props)
end
end
end