refactoring to_xml and signing
This commit is contained in:
parent
823db3ee18
commit
d616e5fae9
14 changed files with 186 additions and 222 deletions
|
|
@ -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
|
||||
|
||||
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
|
||||
pubkey.verify(DIGEST, Base64.decode64(signature), signature_data).tap do |valid|
|
||||
logger.info "event=verify_signature status=complete guid=#{guid} valid=#{valid}"
|
||||
end
|
||||
end
|
||||
|
||||
# @param [Hash] hash data to sign
|
||||
# @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(";")
|
||||
# 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
|
||||
|
||||
# @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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
module DiasporaFederation
|
||||
describe Entities::Relayable do
|
||||
before { skip }
|
||||
|
||||
let(:author_serialized_key) {
|
||||
<<-KEY
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue