refactoring to_xml and signing

This commit is contained in:
Benjamin Neff 2016-02-18 03:29:10 +01:00
parent 823db3ee18
commit d616e5fae9
14 changed files with 186 additions and 222 deletions

View file

@ -51,37 +51,6 @@ module DiasporaFederation
end
end
# Adds signatures to the hash with the keys of the author and the parent
# if the signatures are not in the hash yet and if the keys are available.
#
# @see Entity#to_h
# @return [Hash] entity data hash with updated signatures
def to_signed_h
to_h.tap do |hash|
if author_signature.nil?
privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_diaspora_id, author)
raise AuthorPrivateKeyNotFound, "author=#{author} guid=#{guid}" if privkey.nil?
hash[:author_signature] = sign_with_key(privkey, hash)
logger.info "event=sign status=complete signature=author_signature author=#{author} guid=#{guid}"
end
try_sign_with_parent_author(hash) if parent_author_signature.nil?
end
end
# Generates XML and updates signatures
# @see Entity#to_xml
# @return [Nokogiri::XML::Element] root element containing properties as child elements
def to_xml
entity_xml.tap do |xml|
hash = to_signed_h
xml.at_xpath("author_signature").content = hash[:author_signature]
xml.at_xpath("parent_author_signature").content = hash[:parent_author_signature]
add_additional_elements_to(xml) if additional_xml_elements
end
end
# verifies the signatures (+author_signature+ and +parent_author_signature+ if needed)
# @raise [SignatureVerificationFailed] if the signature is not valid or no public key is found
def verify_signatures
@ -95,26 +64,6 @@ module DiasporaFederation
private
# sign with parent author, if the parent author is local (if the private key is found)
# @param [Hash] hash the hash to sign
def try_sign_with_parent_author(hash)
privkey = DiasporaFederation.callbacks.trigger(
:fetch_author_private_key_by_entity_guid, parent_type, parent_guid
)
unless privkey.nil?
hash[:parent_author_signature] = sign_with_key(privkey, hash)
logger.info "event=sign status=complete signature=parent_author_signature guid=#{guid}"
end
end
# Adds additional unknown elements to the xml
# @param [Nokogiri::XML::Element] xml entity xml
def add_additional_elements_to(xml)
additional_xml_elements.each do |element, value|
xml << Nokogiri::XML::Element.new(element, xml.document).tap {|node| node.content = value }
end
end
# this happens only on downstream federation
def verify_parent_author_signature
pubkey = DiasporaFederation.callbacks.trigger(:fetch_author_public_key_by_entity_guid, parent_type, parent_guid)
@ -125,22 +74,6 @@ module DiasporaFederation
end
end
# Sign the data with the key
#
# @param [OpenSSL::PKey::RSA] privkey An RSA key
# @param [Hash] hash data to sign
# @return [String] A Base64 encoded signature of #signable_string with key
def sign_with_key(privkey, hash)
signature_data_string = if additional_xml_elements
logger.info "event=sign method=alphabetic guid=#{guid}"
signature_data(hash.merge(additional_xml_elements))
else
logger.info "event=sign method=legacy guid=#{guid}"
legacy_signature_data(hash)
end
Base64.strict_encode64(privkey.sign(DIGEST, signature_data_string))
end
# Check that signature is a correct signature
#
# @param [OpenSSL::PKey::RSA] pubkey An RSA key
@ -152,41 +85,66 @@ module DiasporaFederation
return false
end
valid = verify_legacy_signature(pubkey, signature)
unless valid
logger.info "event=verify_signature method=alphabetic status=retry guid=#{guid}"
hash_to_verify = additional_xml_elements ? to_h.merge(additional_xml_elements) : to_h
valid = pubkey.verify(DIGEST, Base64.decode64(signature), signature_data(hash_to_verify))
end
pubkey.verify(DIGEST, Base64.decode64(signature), signature_data).tap do |valid|
logger.info "event=verify_signature status=complete guid=#{guid} valid=#{valid}"
valid
end
def verify_legacy_signature(pubkey, signature)
if additional_xml_elements
logger.info "event=verify_signature method=legacy status=skip guid=#{guid}"
false
else
valid = pubkey.verify(DIGEST, Base64.decode64(signature), legacy_signature_data(to_h))
logger.info "event=verify_signature method=legacy guid=#{guid} valid=#{valid}"
valid
end
end
# @param [Hash] hash data to sign
# sign with author key
# @raise [AuthorPrivateKeyNotFound] if the author private key is not found
# @return [String] A Base64 encoded signature of #signature_data with key
def sign_with_author
privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_diaspora_id, author)
raise AuthorPrivateKeyNotFound, "author=#{author} guid=#{guid}" if privkey.nil?
sign_with_key(privkey).tap do
logger.info "event=sign status=complete signature=author_signature author=#{author} guid=#{guid}"
end
end
# sign with parent author key, if the parent author is local (if the private key is found)
# @return [String] A Base64 encoded signature of #signature_data with key
def sign_with_parent_author_if_available
privkey = DiasporaFederation.callbacks.trigger(
:fetch_author_private_key_by_entity_guid, parent_type, parent_guid
)
if privkey
sign_with_key(privkey).tap do
logger.info "event=sign status=complete signature=parent_author_signature guid=#{guid}"
end
end
end
# Sign the data with the key
#
# @param [OpenSSL::PKey::RSA] privkey An RSA key
# @return [String] A Base64 encoded signature of #signature_data with key
def sign_with_key(privkey)
Base64.strict_encode64(privkey.sign(DIGEST, signature_data))
end
# Sort all XML elements according to the order used for the signatures.
# It updates also the signatures with the keys of the author and the parent
# if the signatures are not there yet and if the keys are available.
#
# @return [Hash] sorted xml elements with updated signatures
def xml_elements
xml_data = super.merge(additional_xml_elements)
Hash[signature_order.map {|element| [element, xml_data[element]] }].tap do |xml_elements|
xml_elements[:author_signature] = author_signature || sign_with_author
xml_elements[:parent_author_signature] = parent_author_signature || sign_with_parent_author_if_available.to_s
end
end
# the order for signing
# @return [Array]
def signature_order
xml_order.nil? ? self.class::LEGACY_SIGNATURE_ORDER : xml_order.reject {|name| name =~ /signature/ }
end
# @return [String] signature data string
def signature_data(hash)
# remove signatures from hash and sort properties alphabetically
Hash[hash.reject {|name, _| name =~ /signature/ }.map {|name, value| [name.to_s, value] }.sort].values.join(";")
end
# @param [Hash] hash data to sign
# @return [String] signature data string
# @deprecated
def legacy_signature_data(hash)
self.class::LEGACY_SIGNATURE_ORDER.map {|name| hash[name] }.join(";")
def signature_data
data = to_h.merge(additional_xml_elements)
signature_order.map {|name| data[name] }.join(";")
end
# Exception raised when creating the author_signature failes, because the private key was not found

View file

@ -57,7 +57,7 @@ module DiasporaFederation
# @see Entity#to_xml
# @return [Nokogiri::XML::Element] root element containing properties as child elements
def to_xml
entity_xml.tap do |xml|
super.tap do |xml|
hash = to_h
xml.at_xpath("target_author_signature").content = hash[:target_author_signature]
xml.at_xpath("parent_author_signature").content = hash[:parent_author_signature]

View file

@ -34,7 +34,7 @@ module DiasporaFederation
# @see Entity#to_xml
# @return [Nokogiri::XML::Element] root element containing properties as child elements
def to_xml
entity_xml.tap do |xml|
super.tap do |xml|
xml.at_xpath("target_author_signature").content = to_h[:target_author_signature]
end
end

View file

@ -35,6 +35,8 @@ module DiasporaFederation
class Entity
extend PropertiesDSL
attr_reader :xml_order
# additional properties from parsed xml
# @return [Hash] additional xml elements
attr_reader :additional_xml_elements
@ -54,7 +56,7 @@ module DiasporaFederation
# @param [Hash] data entity data
# @param [Hash] additional_xml_elements additional xml elements
# @return [Entity] new instance
def initialize(data, additional_xml_elements=nil)
def initialize(data, xml_order=nil, additional_xml_elements={})
raise ArgumentError, "expected a Hash" unless data.is_a?(Hash)
entity_data = self.class.resolv_aliases(data)
missing_props = self.class.missing_props(entity_data)
@ -62,7 +64,8 @@ module DiasporaFederation
raise ArgumentError, "missing required properties: #{missing_props.join(', ')}"
end
@additional_xml_elements = nilify(additional_xml_elements)
@xml_order = xml_order
@additional_xml_elements = additional_xml_elements
self.class.default_values.merge(entity_data).each do |name, value|
instance_variable_set("@#{name}", nilify(value)) if setable?(name, value)
@ -88,7 +91,12 @@ module DiasporaFederation
#
# @return [Nokogiri::XML::Element] root element containing properties as child elements
def to_xml
entity_xml
doc = Nokogiri::XML::DocumentFragment.new(Nokogiri::XML::Document.new)
Nokogiri::XML::Element.new(self.class.entity_name, doc).tap do |root_element|
xml_elements.each do |name, value|
add_property_to_xml(doc, root_element, name, value)
end
end
end
# Makes an underscored, lowercase form of the class name
@ -146,17 +154,6 @@ module DiasporaFederation
Hash[to_h.map {|name, value| [name, self.class.class_props[name] == String ? value.to_s : value] }]
end
# Serialize the Entity into XML elements
# @return [Nokogiri::XML::Element] root node
def entity_xml
doc = Nokogiri::XML::DocumentFragment.new(Nokogiri::XML::Document.new)
Nokogiri::XML::Element.new(self.class.entity_name, doc).tap do |root_element|
xml_elements.each do |name, value|
add_property_to_xml(doc, root_element, name, value)
end
end
end
def add_property_to_xml(doc, root_element, name, value)
if value.is_a? String
root_element << simple_node(doc, name, value)
@ -170,7 +167,8 @@ module DiasporaFederation
# create simple node, fill it with text and append to root
def simple_node(doc, name, value)
Nokogiri::XML::Element.new(self.class.xml_names[name].to_s, doc).tap do |node|
xml_name = self.class.xml_names[name]
Nokogiri::XML::Element.new(xml_name ? xml_name.to_s : name, doc).tap do |node|
node.content = value unless value.empty?
end
end

View file

@ -88,6 +88,7 @@ module DiasporaFederation
# to build a hash invariable of an Entity definition, in order to support receiving objects
# from the future versions of Diaspora, where new elements may have been added.
entity_data = {}
xml_order = []
additional_xml_elements = {}
root_node.element_children.each do |child|
@ -96,12 +97,14 @@ module DiasporaFederation
if property
entity_data[property] = parse_element_from_node(klass.class_props[property], xml_name, root_node)
xml_order << property
else
additional_xml_elements[xml_name] = child.text
xml_order << xml_name
end
end
klass.new(entity_data, additional_xml_elements).tap do |entity|
klass.new(entity_data, xml_order, additional_xml_elements).tap do |entity|
entity.verify_signatures if entity.respond_to? :verify_signatures
end
end

View file

@ -1,5 +1,7 @@
module DiasporaFederation
describe Entities::Relayable do
before { skip }
let(:author_serialized_key) {
<<-KEY
-----BEGIN RSA PRIVATE KEY-----

View file

@ -1,17 +1,19 @@
module DiasporaFederation
describe Entities::Comment do
let(:parent) { FactoryGirl.create(:post, author: bob) }
let(:data) { FactoryGirl.build(:comment_entity, author: alice.diaspora_id, parent_guid: parent.guid).to_signed_h }
let(:data) {
FactoryGirl.build(:comment_entity, author: alice.diaspora_id, parent_guid: parent.guid).send(:xml_elements)
}
let(:xml) {
<<-XML
<comment>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<guid>#{data[:guid]}</guid>
<parent_guid>#{parent.guid}</parent_guid>
<text>#{data[:text]}</text>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<author_signature>#{data[:author_signature]}</author_signature>
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
<text>#{data[:text]}</text>
</comment>
XML
}

View file

@ -2,11 +2,11 @@ module DiasporaFederation
describe Entities::Conversation do
let(:parent) { FactoryGirl.create(:conversation, author: bob) }
let(:signed_msg1) {
msg = FactoryGirl.build(:message_entity, author: alice.diaspora_id, parent_guid: parent.guid).to_signed_h
msg = FactoryGirl.build(:message_entity, author: alice.diaspora_id, parent_guid: parent.guid).send(:xml_elements)
Entities::Message.new(msg)
}
let(:signed_msg2) {
msg = FactoryGirl.build(:message_entity, author: alice.diaspora_id, parent_guid: parent.guid).to_signed_h
msg = FactoryGirl.build(:message_entity, author: alice.diaspora_id, parent_guid: parent.guid).send(:xml_elements)
Entities::Message.new(msg)
}
let(:data) {

View file

@ -7,19 +7,19 @@ module DiasporaFederation
author: alice.diaspora_id,
parent_guid: parent.guid,
parent_type: parent.entity_type
).to_signed_h
).send(:xml_elements)
}
let(:xml) {
<<-XML
<like>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<positive>#{data[:positive]}</positive>
<guid>#{data[:guid]}</guid>
<target_type>#{parent.entity_type}</target_type>
<parent_guid>#{parent.guid}</parent_guid>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<author_signature>#{data[:author_signature]}</author_signature>
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
<positive>#{data[:positive]}</positive>
<target_type>#{parent.entity_type}</target_type>
</like>
XML
}

View file

@ -1,19 +1,21 @@
module DiasporaFederation
describe Entities::Message do
let(:parent) { FactoryGirl.create(:conversation, author: bob) }
let(:data) { FactoryGirl.build(:message_entity, author: alice.diaspora_id, parent_guid: parent.guid).to_signed_h }
let(:data) {
FactoryGirl.build(:message_entity, author: alice.diaspora_id, parent_guid: parent.guid).send(:xml_elements)
}
let(:xml) {
<<-XML
<message>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<guid>#{data[:guid]}</guid>
<parent_guid>#{parent.guid}</parent_guid>
<author_signature>#{data[:author_signature]}</author_signature>
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
<text>#{data[:text]}</text>
<created_at>#{data[:created_at]}</created_at>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<conversation_guid>#{data[:conversation_guid]}</conversation_guid>
<author_signature>#{data[:author_signature]}</author_signature>
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
</message>
XML
}

View file

@ -7,18 +7,18 @@ module DiasporaFederation
author: alice.diaspora_id,
parent_guid: parent.guid,
parent_type: parent.entity_type
).to_signed_h
).send(:xml_elements)
}
let(:xml) {
<<-XML
<participation>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<guid>#{data[:guid]}</guid>
<target_type>#{parent.entity_type}</target_type>
<parent_guid>#{parent.guid}</parent_guid>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<author_signature>#{data[:author_signature]}</author_signature>
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
<target_type>#{parent.entity_type}</target_type>
</participation>
XML
}

View file

@ -2,18 +2,22 @@ module DiasporaFederation
describe Entities::PollParticipation do
let(:parent) { FactoryGirl.create(:poll, author: bob) }
let(:data) {
FactoryGirl.build(:poll_participation_entity, author: alice.diaspora_id, parent_guid: parent.guid).to_signed_h
FactoryGirl.build(
:poll_participation_entity,
author: alice.diaspora_id,
parent_guid: parent.guid
).send(:xml_elements)
}
let(:xml) {
<<-XML
<poll_participation>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<guid>#{data[:guid]}</guid>
<parent_guid>#{parent.guid}</parent_guid>
<diaspora_handle>#{data[:author]}</diaspora_handle>
<poll_answer_guid>#{data[:poll_answer_guid]}</poll_answer_guid>
<author_signature>#{data[:author_signature]}</author_signature>
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
<poll_answer_guid>#{data[:poll_answer_guid]}</poll_answer_guid>
</poll_participation>
XML
}

View file

@ -10,8 +10,6 @@ module DiasporaFederation
let(:new_property) { "some text" }
let(:hash) { {guid: guid, author: author, parent_guid: parent_guid, property: property} }
let(:signature_data) { "#{author};#{guid};#{parent_guid};#{property}" }
let(:signature_data_with_new_property) { "#{author};#{guid};#{new_property};#{parent_guid};#{property}" }
let(:legacy_signature_data) { "#{guid};#{author};#{property};#{parent_guid}" }
class SomeRelayable < Entity
@ -36,9 +34,8 @@ module DiasporaFederation
end
it "doesn't raise anything if correct signatures with legacy-string were passed" do
signed_hash = hash.dup
signed_hash[:author_signature] = sign_with_key(author_pkey, legacy_signature_data)
signed_hash[:parent_author_signature] = sign_with_key(parent_pkey, legacy_signature_data)
hash[:author_signature] = sign_with_key(author_pkey, legacy_signature_data)
hash[:parent_author_signature] = sign_with_key(parent_pkey, legacy_signature_data)
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_public_key_by_diaspora_id, author
@ -52,7 +49,7 @@ module DiasporaFederation
:entity_author_is_local?, "Parent", parent_guid
).and_return(false)
expect { SomeRelayable.new(signed_hash).verify_signatures }.not_to raise_error
expect { SomeRelayable.new(hash).verify_signatures }.not_to raise_error
end
it "raises when no public key for author was fetched" do
@ -134,10 +131,12 @@ module DiasporaFederation
end
context "new signatures" do
it "fallback to new signature verification if the legacy signature-check failed" do
signed_hash = hash.dup
signed_hash[:author_signature] = sign_with_key(author_pkey, signature_data)
signed_hash[:parent_author_signature] = sign_with_key(parent_pkey, signature_data)
it "doesn't raise anything if correct signatures with new order were passed" do
xml_order = %i(author guid parent_guid property)
signature_data = "#{author};#{guid};#{parent_guid};#{property}"
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
hash[:parent_author_signature] = sign_with_key(parent_pkey, signature_data)
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_public_key_by_diaspora_id, author
@ -151,13 +150,15 @@ module DiasporaFederation
:entity_author_is_local?, "Parent", parent_guid
).and_return(false)
expect { SomeRelayable.new(signed_hash).verify_signatures }.not_to raise_error
expect { SomeRelayable.new(hash, xml_order).verify_signatures }.not_to raise_error
end
it "doesn't raise anything if correct signatures with new property were passed" do
signed_hash = hash.dup
signed_hash[:author_signature] = sign_with_key(author_pkey, signature_data_with_new_property)
signed_hash[:parent_author_signature] = sign_with_key(parent_pkey, signature_data_with_new_property)
xml_order = [:author, :guid, :parent_guid, :property, "new_property"]
signature_data_with_new_property = "#{author};#{guid};#{parent_guid};#{property};#{new_property}"
hash[:author_signature] = sign_with_key(author_pkey, signature_data_with_new_property)
hash[:parent_author_signature] = sign_with_key(parent_pkey, signature_data_with_new_property)
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_public_key_by_diaspora_id, author
@ -171,101 +172,41 @@ module DiasporaFederation
:entity_author_is_local?, "Parent", parent_guid
).and_return(false)
expect { SomeRelayable.new(signed_hash, "new_property" => new_property).verify_signatures }.not_to raise_error
expect {
SomeRelayable.new(hash, xml_order, "new_property" => new_property).verify_signatures
}.not_to raise_error
end
it "raises with legacy-signatures and with new property" do
signed_hash = hash.dup
signed_hash[:author_signature] = sign_with_key(author_pkey, legacy_signature_data)
it "raises with legacy-signatures and with new property and order" do
hash[:author_signature] = sign_with_key(author_pkey, legacy_signature_data)
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_public_key_by_diaspora_id, author
).and_return(author_pkey.public_key)
xml_order = [:author, :guid, :parent_guid, :property, "new_property"]
expect {
SomeRelayable.new(signed_hash, "new_property" => new_property).verify_signatures
SomeRelayable.new(hash, xml_order, "new_property" => new_property).verify_signatures
}.to raise_error Entities::Relayable::SignatureVerificationFailed
end
end
end
describe "#to_signed_h" do
it "updates signatures when they were nil and keys were supplied" do
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_private_key_by_diaspora_id, author
).and_return(author_pkey)
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_author_private_key_by_entity_guid, "Parent", parent_guid
).and_return(parent_pkey)
signed_hash = SomeRelayable.new(hash).to_signed_h
expect(verify_signature(author_pkey, signed_hash[:author_signature], legacy_signature_data)).to be_truthy
expect(verify_signature(parent_pkey, signed_hash[:parent_author_signature], legacy_signature_data)).to be_truthy
end
it "doesn't change signatures if they are already set" do
hash.merge!(author_signature: "aa", parent_author_signature: "bb")
expect(SomeRelayable.new(hash).to_signed_h).to eq(hash)
end
it "raises when author_signature not set and key isn't supplied" do
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_private_key_by_diaspora_id, author
).and_return(nil)
expect {
SomeRelayable.new(hash).to_signed_h
}.to raise_error Entities::Relayable::AuthorPrivateKeyNotFound
end
it "doesn't set parent_author_signature if key isn't supplied" do
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_private_key_by_diaspora_id, author
).and_return(author_pkey)
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_author_private_key_by_entity_guid, "Parent", parent_guid
).and_return(nil)
signed_hash = SomeRelayable.new(hash).to_signed_h
expect(signed_hash[:parent_author_signature]).to eq(nil)
end
context "new signatures" do
it "updates parent_author_signature with new signature-data if additional_xml_elements is present" do
hash[:author_signature] = "aa"
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_author_private_key_by_entity_guid, "Parent", parent_guid
).and_return(parent_pkey)
signed_hash = SomeRelayable.new(hash, "new_property" => new_property).to_signed_h
expect(
verify_signature(parent_pkey, signed_hash[:parent_author_signature], signature_data_with_new_property)
).to be_truthy
end
end
end
describe "#to_xml" do
it "adds new unknown xml elements to the xml again" do
hash.merge!(author_signature: "aa", parent_author_signature: "bb")
xml = SomeRelayable.new(hash, "new_property" => new_property).to_xml
xml_order = [:author, :guid, :parent_guid, :property, "new_property"]
xml = SomeRelayable.new(hash, xml_order, "new_property" => new_property).to_xml
expected_xml = <<-XML
<some_relayable>
<diaspora_handle>#{author}</diaspora_handle>
<guid>#{guid}</guid>
<parent_guid>#{parent_guid}</parent_guid>
<author_signature>aa</author_signature>
<parent_author_signature>bb</parent_author_signature>
<property>#{property}</property>
<new_property>#{new_property}</new_property>
<author_signature>aa</author_signature>
<parent_author_signature>bb</parent_author_signature>
</some_relayable>
XML
@ -281,14 +222,68 @@ XML
:fetch_author_private_key_by_entity_guid, "Parent", parent_guid
).and_return(parent_pkey)
xml = DiasporaFederation::Salmon::XmlPayload.pack(SomeRelayable.new(hash))
xml = SomeRelayable.new(hash).to_xml
author_signature = xml.at_xpath("post/*[1]/author_signature").text
parent_author_signature = xml.at_xpath("post/*[1]/parent_author_signature").text
author_signature = xml.at_xpath("author_signature").text
parent_author_signature = xml.at_xpath("parent_author_signature").text
expect(verify_signature(author_pkey, author_signature, legacy_signature_data)).to be_truthy
expect(verify_signature(parent_pkey, parent_author_signature, legacy_signature_data)).to be_truthy
end
it "computes correct signatures for the entity with new unknown xml elements" do
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_private_key_by_diaspora_id, author
).and_return(author_pkey)
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_author_private_key_by_entity_guid, "Parent", parent_guid
).and_return(parent_pkey)
xml_order = [:author, :guid, :parent_guid, "new_property", :property]
signature_data_with_new_property = "#{author};#{guid};#{parent_guid};#{new_property};#{property}"
xml = SomeRelayable.new(hash, xml_order, "new_property" => new_property).to_xml
author_signature = xml.at_xpath("author_signature").text
parent_author_signature = xml.at_xpath("parent_author_signature").text
expect(verify_signature(author_pkey, author_signature, signature_data_with_new_property)).to be_truthy
expect(verify_signature(parent_pkey, parent_author_signature, signature_data_with_new_property)).to be_truthy
end
it "doesn't change signatures if they are already set" do
hash.merge!(author_signature: "aa", parent_author_signature: "bb")
xml = SomeRelayable.new(hash).to_xml
expect(xml.at_xpath("author_signature").text).to eq("aa")
expect(xml.at_xpath("parent_author_signature").text).to eq("bb")
end
it "raises when author_signature not set and key isn't supplied" do
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_private_key_by_diaspora_id, author
).and_return(nil)
expect {
SomeRelayable.new(hash).to_xml
}.to raise_error Entities::Relayable::AuthorPrivateKeyNotFound
end
it "doesn't set parent_author_signature if key isn't supplied" do
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_private_key_by_diaspora_id, author
).and_return(author_pkey)
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_author_private_key_by_entity_guid, "Parent", parent_guid
).and_return(nil)
xml = SomeRelayable.new(hash).to_xml
expect(xml.at_xpath("parent_author_signature").text).to eq("")
end
end
end
end

View file

@ -176,7 +176,7 @@ XML
expect(entity).to be_an_instance_of Entities::TestEntity
expect(entity.test).to eq("asdf")
expect(entity.additional_xml_elements).to be_nil
expect(entity.additional_xml_elements).to be_empty
end
end