parent
1238fe0384
commit
3fcea8b188
10 changed files with 54 additions and 176 deletions
|
|
@ -33,7 +33,6 @@ See also: [Relayable][relayable]
|
||||||
<parent_guid>bb8371f0b1c901342ebd55853a9b5d75</parent_guid>
|
<parent_guid>bb8371f0b1c901342ebd55853a9b5d75</parent_guid>
|
||||||
<status>accepted</status>
|
<status>accepted</status>
|
||||||
<author_signature>dT6KbT7kp0bE+s3//ZErxO1wvVIqtD0lY67i81+dO43B4D2m5kjCdzW240eWt/jZmcHIsdxXf4WHNdrb6ZDnamA8I1FUVnLjHA9xexBITQsSLXrcV88UdammSmmOxl1Ac4VUXqFpdavm6a7/MwOJ7+JHP8TbUO9siN+hMfgUbtY=</author_signature>
|
<author_signature>dT6KbT7kp0bE+s3//ZErxO1wvVIqtD0lY67i81+dO43B4D2m5kjCdzW240eWt/jZmcHIsdxXf4WHNdrb6ZDnamA8I1FUVnLjHA9xexBITQsSLXrcV88UdammSmmOxl1Ac4VUXqFpdavm6a7/MwOJ7+JHP8TbUO9siN+hMfgUbtY=</author_signature>
|
||||||
<parent_author_signature/>
|
|
||||||
</event_participation>
|
</event_participation>
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
|
@ -46,7 +45,6 @@ See also: [Relayable][relayable]
|
||||||
<parent_guid>bb8371f0b1c901342ebd55853a9b5d75</parent_guid>
|
<parent_guid>bb8371f0b1c901342ebd55853a9b5d75</parent_guid>
|
||||||
<status>accepted</status>
|
<status>accepted</status>
|
||||||
<author_signature>dT6KbT7kp0bE+s3//ZErxO1wvVIqtD0lY67i81+dO43B4D2m5kjCdzW240eWt/jZmcHIsdxXf4WHNdrb6ZDnamA8I1FUVnLjHA9xexBITQsSLXrcV88UdammSmmOxl1Ac4VUXqFpdavm6a7/MwOJ7+JHP8TbUO9siN+hMfgUbtY=</author_signature>
|
<author_signature>dT6KbT7kp0bE+s3//ZErxO1wvVIqtD0lY67i81+dO43B4D2m5kjCdzW240eWt/jZmcHIsdxXf4WHNdrb6ZDnamA8I1FUVnLjHA9xexBITQsSLXrcV88UdammSmmOxl1Ac4VUXqFpdavm6a7/MwOJ7+JHP8TbUO9siN+hMfgUbtY=</author_signature>
|
||||||
<parent_author_signature>gWasNPpSnMcKBIMWyzfoVO6sr8eRYkhUqy3PIkkh53n/ki+DM9mnh3ayotI0+6un9aq1N3XkS7Vn05ZD3+nHVby6i21XkYgPnbD8pWYuBBj7VGPyahT70BUs/vSvY8KX8V3wYfsPsaiAgJsAFg2UHYdY3r4/oWdIIbBZc21O3zk=</parent_author_signature>
|
|
||||||
</event_participation>
|
</event_participation>
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,6 @@ module DiasporaFederation
|
||||||
# a target pod.
|
# a target pod.
|
||||||
# @return [String] author signature
|
# @return [String] author signature
|
||||||
#
|
#
|
||||||
# @!attribute [r] parent_author_signature
|
|
||||||
# Contains a signature of the entity using the private key of the author of a parent post.
|
|
||||||
# @deprecated This signature isn't required anymore, because we can check the signature from
|
|
||||||
# the parent author in the MagicEnvelope.
|
|
||||||
# @return [String] parent author signature
|
|
||||||
#
|
|
||||||
# @!attribute [r] parent
|
# @!attribute [r] parent
|
||||||
# Meta information about the parent object
|
# Meta information about the parent object
|
||||||
# @return [RelatedEntity] parent entity
|
# @return [RelatedEntity] parent entity
|
||||||
|
|
@ -52,7 +46,6 @@ module DiasporaFederation
|
||||||
property :guid, :string
|
property :guid, :string
|
||||||
property :parent_guid, :string
|
property :parent_guid, :string
|
||||||
property :author_signature, :string, default: nil
|
property :author_signature, :string, default: nil
|
||||||
property :parent_author_signature, :string, default: nil
|
|
||||||
entity :parent, Entities::RelatedEntity
|
entity :parent, Entities::RelatedEntity
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -67,7 +60,7 @@ module DiasporaFederation
|
||||||
# @see DiasporaFederation::Entity#initialize
|
# @see DiasporaFederation::Entity#initialize
|
||||||
def initialize(data, signature_order=nil, additional_data={})
|
def initialize(data, signature_order=nil, additional_data={})
|
||||||
self.signature_order = signature_order if signature_order
|
self.signature_order = signature_order if signature_order
|
||||||
@additional_data = additional_data
|
self.additional_data = additional_data
|
||||||
|
|
||||||
super(data)
|
super(data)
|
||||||
end
|
end
|
||||||
|
|
@ -104,7 +97,7 @@ module DiasporaFederation
|
||||||
def signature_order
|
def signature_order
|
||||||
@signature_order || self.class.class_props.keys.reject {|key|
|
@signature_order || self.class.class_props.keys.reject {|key|
|
||||||
self.class.optional_props.include?(key) && public_send(key).nil?
|
self.class.optional_props.include?(key) && public_send(key).nil?
|
||||||
} - %i[author_signature parent_author_signature parent]
|
} - %i[author_signature parent]
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
@ -121,17 +114,6 @@ module DiasporaFederation
|
||||||
end
|
end
|
||||||
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_private_key, parent.root.author)
|
|
||||||
return unless privkey
|
|
||||||
|
|
||||||
sign_with_key(privkey).tap do
|
|
||||||
logger.info "event=sign status=complete signature=parent_author_signature obj=#{self}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update the signatures with the keys of the author and the parent
|
# Update the signatures with the keys of the author and the parent
|
||||||
# if the signatures are not there yet and if the keys are available.
|
# if the signatures are not there yet and if the keys are available.
|
||||||
#
|
#
|
||||||
|
|
@ -139,7 +121,6 @@ module DiasporaFederation
|
||||||
def enriched_properties
|
def enriched_properties
|
||||||
super.merge(additional_data).tap do |hash|
|
super.merge(additional_data).tap do |hash|
|
||||||
hash[:author_signature] = author_signature || sign_with_author
|
hash[:author_signature] = author_signature || sign_with_author
|
||||||
hash.delete(:parent_author_signature)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -147,10 +128,8 @@ module DiasporaFederation
|
||||||
#
|
#
|
||||||
# @return [Hash] sorted xml elements
|
# @return [Hash] sorted xml elements
|
||||||
def xml_elements
|
def xml_elements
|
||||||
data = super.tap do |hash|
|
data = super
|
||||||
hash[:parent_author_signature] = parent_author_signature || sign_with_parent_author_if_available.to_s
|
order = signature_order + %i[author_signature]
|
||||||
end
|
|
||||||
order = signature_order + %i[author_signature parent_author_signature]
|
|
||||||
order.map {|element| [element, data[element].to_s] }.to_h
|
order.map {|element| [element, data[element].to_s] }.to_h
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -160,6 +139,10 @@ module DiasporaFederation
|
||||||
.map {|name| prop_names.include?(name) ? name.to_sym : name }
|
.map {|name| prop_names.include?(name) ? name.to_sym : name }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def additional_data=(additional_data)
|
||||||
|
@additional_data = additional_data.reject {|name, _| name =~ /signature/ }
|
||||||
|
end
|
||||||
|
|
||||||
# @return [String] signature data string
|
# @return [String] signature data string
|
||||||
def signature_data
|
def signature_data
|
||||||
data = normalized_properties.merge(additional_data)
|
data = normalized_properties.merge(additional_data)
|
||||||
|
|
|
||||||
|
|
@ -27,31 +27,6 @@ module DiasporaFederation
|
||||||
# g6zpg1zxGahpmxwqFQIDAQAB
|
# g6zpg1zxGahpmxwqFQIDAQAB
|
||||||
# -----END PUBLIC KEY-----
|
# -----END PUBLIC KEY-----
|
||||||
|
|
||||||
let(:parent_serialized_key) { <<~KEY }
|
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIICXgIBAAKBgQDrOvW1UArKoUOg54XWXcTD3jU0zKG3Pm9IeaEzfQtApogQ3+M/
|
|
||||||
F9nz0i3q8UhTDEPBQ3hMbqJ/4qfY+wFulxMR58DbqxFx9QcNZISUd0CPx/fJOYMx
|
|
||||||
R7bygTbiCet4FAiyMjxOX3Oei/DedUNps1RAP1bu+80iibze/Kk9BgMm0QIDAQAB
|
|
||||||
AoGAMHvikRCCaOl8SvnteBWzrLtsNAnJez9/KG0JcNdhLl4kxXWgHS0JW1wC4t4A
|
|
||||||
jj2E6ZzCet6C1+Ebv3lc/jJdV3pCK3wgX0YAt/oBW5kcuvpLHLSWusWHnHkYU+qO
|
|
||||||
4SdC3bRhdLV9o3u/oCWzmdeKTdqIyNd2yAbb3W1TsD4EsQECQQD6w+vWVKhWbVOj
|
|
||||||
Ky3ZkLCxPcWNUt+7OIzDA1OLKhdhe44hIoRMfDT6iLK3sJTSjgOv0OFTfsdOqh5y
|
|
||||||
ZqYp/CTpAkEA8CQFKkAYt4qG1lKMPsU/Tme0/Z24VozDRnyw7r663f0De+25kXGY
|
|
||||||
PSBiOHYcAE6poYQEtR/leLTSaG3YZm7hqQJBAOLAWLg1Uwbb0v4/pDUQlgWfQsy4
|
|
||||||
/KAx0W7hyiCTzhKTBAFIUfNLeSh2hYx+ewQt8H2B1s6GXDjwsZlm4qgiXUkCQQC9
|
|
||||||
B12ZeIL8V2r0Yl5LOvEuQqxRx0lHt94vKhAMns5x16xabTLZrlVsKIWodDBufX1B
|
|
||||||
yq359XWooo3N7kmduEKhAkEAppzKLuVtX1XPL4VZBex/M2ewngjkSg964BvxIBwv
|
|
||||||
bFzeSqlMpnbEoOJ9hhx6CsP6Y7V19DRRXi0XgwcAjHLz8g==
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
KEY
|
|
||||||
let(:parent_key) { OpenSSL::PKey::RSA.new(parent_serialized_key) }
|
|
||||||
# -----BEGIN PUBLIC KEY-----
|
|
||||||
# MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrOvW1UArKoUOg54XWXcTD3jU0
|
|
||||||
# zKG3Pm9IeaEzfQtApogQ3+M/F9nz0i3q8UhTDEPBQ3hMbqJ/4qfY+wFulxMR58Db
|
|
||||||
# qxFx9QcNZISUd0CPx/fJOYMxR7bygTbiCet4FAiyMjxOX3Oei/DedUNps1RAP1bu
|
|
||||||
# +80iibze/Kk9BgMm0QIDAQAB
|
|
||||||
# -----END PUBLIC KEY-----
|
|
||||||
|
|
||||||
let(:author) { "alice@pod-a.org" }
|
let(:author) { "alice@pod-a.org" }
|
||||||
let(:guid) { "e21589b0b41101333b870f77ba60fa73" }
|
let(:guid) { "e21589b0b41101333b870f77ba60fa73" }
|
||||||
let(:parent_guid) { "9e269ae0b41201333b8c0f77ba60fa73" }
|
let(:parent_guid) { "9e269ae0b41201333b8c0f77ba60fa73" }
|
||||||
|
|
@ -104,7 +79,6 @@ module DiasporaFederation
|
||||||
<parent_guid>9e269ae0b41201333b8c0f77ba60fa73</parent_guid>
|
<parent_guid>9e269ae0b41201333b8c0f77ba60fa73</parent_guid>
|
||||||
<text>this is a very informative comment</text>
|
<text>this is a very informative comment</text>
|
||||||
<author_signature>SQbLeqsEpFmSl74G1fFJXKQcsq6jp5B2ZjmfEOF/LbBccYP2oZEyEqOq18K3Fa71OYTp6Nddb38hCmHWWHvnGUltGfxKBnQ0WHafJUi40VM4VmeRoU8cac6m+1hslwe5SNmK6oh47EV3mRCXlgGGjLIrw7iEwjKL2g9x1gkNp2s=</author_signature>
|
<author_signature>SQbLeqsEpFmSl74G1fFJXKQcsq6jp5B2ZjmfEOF/LbBccYP2oZEyEqOq18K3Fa71OYTp6Nddb38hCmHWWHvnGUltGfxKBnQ0WHafJUi40VM4VmeRoU8cac6m+1hslwe5SNmK6oh47EV3mRCXlgGGjLIrw7iEwjKL2g9x1gkNp2s=</author_signature>
|
||||||
<parent_author_signature>hWsagsczmZD6d36d6MFdTt3hKAdnRtupSIU6464G2kkMJ+WlExxMgbF6kWR+jVCBTeKipWCYK3Arnj0YkuIZM9d14bJGVMTsW/ZzNfJ69bXZhsyawI8dPnZnLVydo+hU/XmGJBEuh2TOj9Emq6/HCYiWzPTF5qhYAtyJ1oxJ4Yk=</parent_author_signature>
|
|
||||||
</comment>
|
</comment>
|
||||||
XML
|
XML
|
||||||
let(:new_data_comment_xml_bob) { <<~XML }
|
let(:new_data_comment_xml_bob) { <<~XML }
|
||||||
|
|
@ -115,7 +89,6 @@ module DiasporaFederation
|
||||||
<text>this is a very informative comment</text>
|
<text>this is a very informative comment</text>
|
||||||
<new_data>foobar</new_data>
|
<new_data>foobar</new_data>
|
||||||
<author_signature>SFYXSvCX/DhTFiOUALp2Nf1kfNkGKXrnoBPikAyhaIogGydVBm+8tIlu1U/vsnpyKO3yfC3JReJ00/UBd4J16VO1IxStntq8NUqbSv4me5A/6kdK9Xg6eYbXrqQGm8fUQ5Xuh2UzeB71p7SVySXX3OZHVe0dvHCxH/lsfSDpEjc=</author_signature>
|
<author_signature>SFYXSvCX/DhTFiOUALp2Nf1kfNkGKXrnoBPikAyhaIogGydVBm+8tIlu1U/vsnpyKO3yfC3JReJ00/UBd4J16VO1IxStntq8NUqbSv4me5A/6kdK9Xg6eYbXrqQGm8fUQ5Xuh2UzeB71p7SVySXX3OZHVe0dvHCxH/lsfSDpEjc=</author_signature>
|
||||||
<parent_author_signature>NxXuEUVeXwUMR77osIbaNlp2oB3bpl8rBEFgQoO6cnoN5ewDbiGADK0x6EhcmJptjwhGVcZiNJNpq7k3/pjJtKaH++3ToCAtcuZoIKwPDsneLnjPhVjE2GXM1TiZKwoHrq41qSp/8Vl5UPbtC6sPiOzIvPKaILXUG8XCiVWuB0M=</parent_author_signature>
|
|
||||||
</comment>
|
</comment>
|
||||||
XML
|
XML
|
||||||
|
|
||||||
|
|
@ -123,14 +96,12 @@ module DiasporaFederation
|
||||||
context "test-data creation" do
|
context "test-data creation" do
|
||||||
it "creates comment xml" do
|
it "creates comment xml" do
|
||||||
expect_callback(:fetch_private_key, author).and_return(author_key)
|
expect_callback(:fetch_private_key, author).and_return(author_key)
|
||||||
expect_callback(:fetch_private_key, parent.author).and_return(nil)
|
|
||||||
|
|
||||||
comment.to_xml
|
comment.to_xml
|
||||||
end
|
end
|
||||||
|
|
||||||
it "creates relayed comment xml" do
|
it "creates relayed comment xml" do
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_key.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_key.public_key)
|
||||||
expect_callback(:fetch_private_key, parent.author).and_return(parent_key)
|
|
||||||
expect_callback(:fetch_related_entity, "Post", parent_guid).and_return(parent)
|
expect_callback(:fetch_related_entity, "Post", parent_guid).and_return(parent)
|
||||||
|
|
||||||
xml = Nokogiri::XML(new_data_comment_xml_alice).root
|
xml = Nokogiri::XML(new_data_comment_xml_alice).root
|
||||||
|
|
@ -141,7 +112,6 @@ module DiasporaFederation
|
||||||
context "relaying on bobs pod" do
|
context "relaying on bobs pod" do
|
||||||
before do
|
before do
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_key.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_key.public_key)
|
||||||
expect_callback(:fetch_private_key, parent.author).and_return(parent_key)
|
|
||||||
expect_callback(:fetch_related_entity, "Post", parent_guid).and_return(parent)
|
expect_callback(:fetch_related_entity, "Post", parent_guid).and_return(parent)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ module DiasporaFederation
|
||||||
<created_at>#{data[:created_at].utc.iso8601}</created_at>
|
<created_at>#{data[:created_at].utc.iso8601}</created_at>
|
||||||
<edited_at>#{data[:edited_at].utc.iso8601}</edited_at>
|
<edited_at>#{data[:edited_at].utc.iso8601}</edited_at>
|
||||||
<author_signature>#{data[:author_signature]}</author_signature>
|
<author_signature>#{data[:author_signature]}</author_signature>
|
||||||
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
|
|
||||||
</comment>
|
</comment>
|
||||||
XML
|
XML
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ module DiasporaFederation
|
||||||
<status>#{data[:status]}</status>
|
<status>#{data[:status]}</status>
|
||||||
<edited_at>#{data[:edited_at].utc.iso8601}</edited_at>
|
<edited_at>#{data[:edited_at].utc.iso8601}</edited_at>
|
||||||
<author_signature>#{data[:author_signature]}</author_signature>
|
<author_signature>#{data[:author_signature]}</author_signature>
|
||||||
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
|
|
||||||
</event_participation>
|
</event_participation>
|
||||||
XML
|
XML
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ module DiasporaFederation
|
||||||
<parent_type>#{parent.entity_type}</parent_type>
|
<parent_type>#{parent.entity_type}</parent_type>
|
||||||
<positive>#{data[:positive]}</positive>
|
<positive>#{data[:positive]}</positive>
|
||||||
<author_signature>#{data[:author_signature]}</author_signature>
|
<author_signature>#{data[:author_signature]}</author_signature>
|
||||||
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
|
|
||||||
</like>
|
</like>
|
||||||
XML
|
XML
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ module DiasporaFederation
|
||||||
<parent_guid>#{parent.guid}</parent_guid>
|
<parent_guid>#{parent.guid}</parent_guid>
|
||||||
<poll_answer_guid>#{data[:poll_answer_guid]}</poll_answer_guid>
|
<poll_answer_guid>#{data[:poll_answer_guid]}</poll_answer_guid>
|
||||||
<author_signature>#{data[:author_signature]}</author_signature>
|
<author_signature>#{data[:author_signature]}</author_signature>
|
||||||
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
|
|
||||||
</poll_participation>
|
</poll_participation>
|
||||||
XML
|
XML
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
module DiasporaFederation
|
module DiasporaFederation
|
||||||
describe Entities::Relayable do
|
describe Entities::Relayable do
|
||||||
let(:author_pkey) { OpenSSL::PKey::RSA.generate(1024) }
|
let(:author_pkey) { OpenSSL::PKey::RSA.generate(1024) }
|
||||||
let(:parent_pkey) { OpenSSL::PKey::RSA.generate(1024) }
|
|
||||||
|
|
||||||
let(:guid) { Fabricate.sequence(:guid) }
|
let(:guid) { Fabricate.sequence(:guid) }
|
||||||
let(:parent_guid) { Fabricate.sequence(:guid) }
|
let(:parent_guid) { Fabricate.sequence(:guid) }
|
||||||
|
|
@ -13,24 +12,32 @@ module DiasporaFederation
|
||||||
let(:local_parent) { Fabricate(:related_entity, author: bob.diaspora_id) }
|
let(:local_parent) { Fabricate(:related_entity, author: bob.diaspora_id) }
|
||||||
let(:remote_parent) { Fabricate(:related_entity, author: bob.diaspora_id, local: false) }
|
let(:remote_parent) { Fabricate(:related_entity, author: bob.diaspora_id, local: false) }
|
||||||
let(:hash) { {guid: guid, author: author, parent_guid: parent_guid, parent: local_parent, property: property} }
|
let(:hash) { {guid: guid, author: author, parent_guid: parent_guid, parent: local_parent, property: property} }
|
||||||
let(:hash_with_fake_signatures) { hash.merge!(author_signature: "aa", parent_author_signature: "bb") }
|
let(:hash_with_fake_signatures) { hash.merge!(author_signature: "aa") }
|
||||||
|
|
||||||
let(:signature_order) { %i[author guid parent_guid property] }
|
let(:signature_order) { %i[author guid parent_guid property] }
|
||||||
let(:signature_data) { "#{author};#{guid};#{parent_guid};#{property}" }
|
let(:signature_data) { "#{author};#{guid};#{parent_guid};#{property}" }
|
||||||
|
|
||||||
describe "#initialize" do
|
describe "#initialize" do
|
||||||
it "filters signatures from order" do
|
it "filters signatures from order" do
|
||||||
signature_order = [:author, :guid, :parent_guid, :property, "new_property", :author_signature]
|
signature_order =
|
||||||
|
[:author, :guid, :parent_guid, :property, "new_property", :author_signature, "parent_author_signature"]
|
||||||
|
|
||||||
expect(Entities::SomeRelayable.new(hash, signature_order).signature_order)
|
expect(Entities::SomeRelayable.new(hash, signature_order).signature_order)
|
||||||
.to eq([:author, :guid, :parent_guid, :property, "new_property"])
|
.to eq([:author, :guid, :parent_guid, :property, "new_property"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "filters signatures from additional_data" do
|
||||||
|
signature_order = [:author, :guid, :parent_guid, :property, "new_property"]
|
||||||
|
additional_data = {"new_property" => "foobar", "parent_author_signature" => "bb"}
|
||||||
|
|
||||||
|
expect(Entities::SomeRelayable.new(hash, signature_order, additional_data).additional_data)
|
||||||
|
.to eq("new_property" => "foobar")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#verify_signature" do
|
describe "#verify_signature" do
|
||||||
it "doesn't raise anything if correct signatures were passed" do
|
it "doesn't raise anything if correct author_signature was passed" do
|
||||||
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
||||||
hash[:parent_author_signature] = sign_with_key(parent_pkey, signature_data)
|
|
||||||
hash[:parent] = remote_parent
|
hash[:parent] = remote_parent
|
||||||
|
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
||||||
|
|
@ -38,12 +45,11 @@ module DiasporaFederation
|
||||||
expect { Entities::SomeRelayable.new(hash, signature_order).verify_signature }.not_to raise_error
|
expect { Entities::SomeRelayable.new(hash, signature_order).verify_signature }.not_to raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't raise anything if correct signatures with new property were passed" do
|
it "doesn't raise anything if correct author_signature with new property was passed" do
|
||||||
signature_order = [:author, :guid, :parent_guid, :property, "new_property"]
|
signature_order = [:author, :guid, :parent_guid, :property, "new_property"]
|
||||||
signature_data_with_new_property = "#{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[: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)
|
|
||||||
hash[:parent] = remote_parent
|
hash[:parent] = remote_parent
|
||||||
|
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
||||||
|
|
@ -99,26 +105,6 @@ module DiasporaFederation
|
||||||
Entities::SomeRelayable.new(hash, signature_order).verify_signature
|
Entities::SomeRelayable.new(hash, signature_order).verify_signature
|
||||||
}.to raise_error Entities::Relayable::SignatureVerificationFailed
|
}.to raise_error Entities::Relayable::SignatureVerificationFailed
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't raise when no parent author signature was passed" do
|
|
||||||
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
|
||||||
hash[:parent_author_signature] = nil
|
|
||||||
hash[:parent] = remote_parent
|
|
||||||
|
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
|
||||||
|
|
||||||
expect { Entities::SomeRelayable.new(hash, signature_order).verify_signature }.not_to raise_error
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't raise when no parent author signature was passed and we're on upstream federation" do
|
|
||||||
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
|
||||||
hash[:parent_author_signature] = nil
|
|
||||||
hash[:parent] = local_parent
|
|
||||||
|
|
||||||
expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key)
|
|
||||||
|
|
||||||
expect { Entities::SomeRelayable.new(hash, signature_order).verify_signature }.not_to raise_error
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#to_xml" do
|
describe "#to_xml" do
|
||||||
|
|
@ -130,7 +116,6 @@ module DiasporaFederation
|
||||||
<property>#{property}</property>
|
<property>#{property}</property>
|
||||||
<new_property>#{new_property}</new_property>
|
<new_property>#{new_property}</new_property>
|
||||||
<author_signature>aa</author_signature>
|
<author_signature>aa</author_signature>
|
||||||
<parent_author_signature>bb</parent_author_signature>
|
|
||||||
</some_relayable>
|
</some_relayable>
|
||||||
XML
|
XML
|
||||||
|
|
||||||
|
|
@ -169,7 +154,6 @@ module DiasporaFederation
|
||||||
<property/>
|
<property/>
|
||||||
<new_property>#{new_property}</new_property>
|
<new_property>#{new_property}</new_property>
|
||||||
<author_signature>aa</author_signature>
|
<author_signature>aa</author_signature>
|
||||||
<parent_author_signature>bb</parent_author_signature>
|
|
||||||
</some_relayable>
|
</some_relayable>
|
||||||
XML
|
XML
|
||||||
|
|
||||||
|
|
@ -188,7 +172,6 @@ module DiasporaFederation
|
||||||
<guid>#{guid}</guid>
|
<guid>#{guid}</guid>
|
||||||
<parent_guid>#{parent_guid}</parent_guid>
|
<parent_guid>#{parent_guid}</parent_guid>
|
||||||
<author_signature>aa</author_signature>
|
<author_signature>aa</author_signature>
|
||||||
<parent_author_signature>bb</parent_author_signature>
|
|
||||||
</some_relayable>
|
</some_relayable>
|
||||||
XML
|
XML
|
||||||
|
|
||||||
|
|
@ -197,22 +180,16 @@ module DiasporaFederation
|
||||||
expect(xml.to_s.strip).to eq(expected_xml.strip)
|
expect(xml.to_s.strip).to eq(expected_xml.strip)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "computes correct signatures for the entity" do
|
it "computes correct author_signature for the entity" do
|
||||||
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
||||||
expect_callback(:fetch_private_key, local_parent.author).and_return(parent_pkey)
|
|
||||||
|
|
||||||
xml = Entities::SomeRelayable.new(hash).to_xml
|
xml = Entities::SomeRelayable.new(hash).to_xml
|
||||||
|
|
||||||
author_signature = xml.at_xpath("author_signature").text
|
expect(verify_signature(author_pkey, xml.at_xpath("author_signature").text, signature_data)).to be_truthy
|
||||||
parent_author_signature = xml.at_xpath("parent_author_signature").text
|
|
||||||
|
|
||||||
expect(verify_signature(author_pkey, author_signature, signature_data)).to be_truthy
|
|
||||||
expect(verify_signature(parent_pkey, parent_author_signature, signature_data)).to be_truthy
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "computes correct signatures for the entity with invalid XML characters" do
|
it "computes correct author_signature for the entity with invalid XML characters" do
|
||||||
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
||||||
expect_callback(:fetch_private_key, local_parent.author).and_return(parent_pkey)
|
|
||||||
|
|
||||||
invalid_property = "asdfasdf asdf💩asdf\nasdf"
|
invalid_property = "asdfasdf asdf💩asdf\nasdf"
|
||||||
signature_data_with_fixed_property = "#{author};#{guid};#{parent_guid};asdf<64>asdf asdf💩asdf\nasdf"
|
signature_data_with_fixed_property = "#{author};#{guid};#{parent_guid};asdf<64>asdf asdf💩asdf\nasdf"
|
||||||
|
|
@ -220,31 +197,22 @@ module DiasporaFederation
|
||||||
xml = Entities::SomeRelayable.new(hash.merge(property: invalid_property)).to_xml
|
xml = Entities::SomeRelayable.new(hash.merge(property: invalid_property)).to_xml
|
||||||
|
|
||||||
author_signature = xml.at_xpath("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, signature_data_with_fixed_property)).to be_truthy
|
expect(verify_signature(author_pkey, author_signature, signature_data_with_fixed_property)).to be_truthy
|
||||||
expect(verify_signature(parent_pkey, parent_author_signature, signature_data_with_fixed_property)).to be_truthy
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "computes correct signatures for the entity when the parent is a relayable itself" do
|
it "computes correct author_signature for the entity when the parent is a relayable itself" do
|
||||||
intermediate_author = Fabricate.sequence(:diaspora_id)
|
intermediate_author = Fabricate.sequence(:diaspora_id)
|
||||||
parent = Fabricate(:related_entity, author: intermediate_author, local: true, parent: local_parent)
|
parent = Fabricate(:related_entity, author: intermediate_author, local: true, parent: local_parent)
|
||||||
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
||||||
expect_callback(:fetch_private_key, local_parent.author).and_return(parent_pkey)
|
|
||||||
expect(DiasporaFederation.callbacks).not_to receive(:trigger).with(:fetch_private_key, intermediate_author)
|
expect(DiasporaFederation.callbacks).not_to receive(:trigger).with(:fetch_private_key, intermediate_author)
|
||||||
|
|
||||||
xml = Entities::SomeRelayable.new(hash.merge(parent: parent)).to_xml
|
xml = Entities::SomeRelayable.new(hash.merge(parent: parent)).to_xml
|
||||||
|
|
||||||
author_signature = xml.at_xpath("author_signature").text
|
expect(verify_signature(author_pkey, xml.at_xpath("author_signature").text, signature_data)).to be_truthy
|
||||||
parent_author_signature = xml.at_xpath("parent_author_signature").text
|
|
||||||
|
|
||||||
expect(verify_signature(author_pkey, author_signature, signature_data)).to be_truthy
|
|
||||||
expect(verify_signature(parent_pkey, parent_author_signature, signature_data)).to be_truthy
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "computes correct signatures for the entity with new unknown xml elements" do
|
it "computes correct author_signature for the entity with new unknown xml elements" do
|
||||||
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
||||||
expect_callback(:fetch_private_key, local_parent.author).and_return(parent_pkey)
|
|
||||||
|
|
||||||
signature_order = [:author, :guid, :parent_guid, "new_property", :property]
|
signature_order = [:author, :guid, :parent_guid, "new_property", :property]
|
||||||
signature_data_with_new_property = "#{author};#{guid};#{parent_guid};#{new_property};#{property}"
|
signature_data_with_new_property = "#{author};#{guid};#{parent_guid};#{new_property};#{property}"
|
||||||
|
|
@ -252,17 +220,13 @@ module DiasporaFederation
|
||||||
xml = Entities::SomeRelayable.new(hash, signature_order, "new_property" => new_property).to_xml
|
xml = Entities::SomeRelayable.new(hash, signature_order, "new_property" => new_property).to_xml
|
||||||
|
|
||||||
author_signature = xml.at_xpath("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, signature_data_with_new_property)).to be_truthy
|
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
|
end
|
||||||
|
|
||||||
it "doesn't change signatures if they are already set" do
|
it "doesn't change author_signature if it is already set" do
|
||||||
xml = Entities::SomeRelayable.new(hash_with_fake_signatures).to_xml
|
xml = Entities::SomeRelayable.new(hash_with_fake_signatures).to_xml
|
||||||
|
|
||||||
expect(xml.at_xpath("author_signature").text).to eq("aa")
|
expect(xml.at_xpath("author_signature").text).to eq("aa")
|
||||||
expect(xml.at_xpath("parent_author_signature").text).to eq("bb")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises when author_signature not set and key isn't supplied" do
|
it "raises when author_signature not set and key isn't supplied" do
|
||||||
|
|
@ -273,15 +237,6 @@ module DiasporaFederation
|
||||||
}.to raise_error Entities::Relayable::AuthorPrivateKeyNotFound
|
}.to raise_error Entities::Relayable::AuthorPrivateKeyNotFound
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't set parent_author_signature if key isn't supplied" do
|
|
||||||
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
|
||||||
expect_callback(:fetch_private_key, local_parent.author).and_return(nil)
|
|
||||||
|
|
||||||
xml = Entities::SomeRelayable.new(hash).to_xml
|
|
||||||
|
|
||||||
expect(xml.at_xpath("parent_author_signature").text).to eq("")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "adds 'false' booleans" do
|
it "adds 'false' booleans" do
|
||||||
expected_xml = <<~XML
|
expected_xml = <<~XML
|
||||||
<test_relayable_with_boolean>
|
<test_relayable_with_boolean>
|
||||||
|
|
@ -290,7 +245,6 @@ module DiasporaFederation
|
||||||
<parent_guid>#{parent_guid}</parent_guid>
|
<parent_guid>#{parent_guid}</parent_guid>
|
||||||
<test>false</test>
|
<test>false</test>
|
||||||
<author_signature>aa</author_signature>
|
<author_signature>aa</author_signature>
|
||||||
<parent_author_signature>bb</parent_author_signature>
|
|
||||||
</test_relayable_with_boolean>
|
</test_relayable_with_boolean>
|
||||||
XML
|
XML
|
||||||
|
|
||||||
|
|
@ -316,7 +270,6 @@ module DiasporaFederation
|
||||||
<new_property>#{new_property}</new_property>
|
<new_property>#{new_property}</new_property>
|
||||||
<property>#{property}</property>
|
<property>#{property}</property>
|
||||||
<author_signature>#{sign_with_key(author_pkey, new_signature_data)}</author_signature>
|
<author_signature>#{sign_with_key(author_pkey, new_signature_data)}</author_signature>
|
||||||
<parent_author_signature>#{sign_with_key(parent_pkey, new_signature_data)}</parent_author_signature>
|
|
||||||
</some_relayable>
|
</some_relayable>
|
||||||
XML
|
XML
|
||||||
|
|
||||||
|
|
@ -338,7 +291,6 @@ module DiasporaFederation
|
||||||
|
|
||||||
it "creates Entity with empty 'additional_data' if the xml has only known properties" do
|
it "creates Entity with empty 'additional_data' if the xml has only known properties" do
|
||||||
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
hash[:author_signature] = sign_with_key(author_pkey, signature_data)
|
||||||
hash[:parent_author_signature] = sign_with_key(parent_pkey, signature_data)
|
|
||||||
|
|
||||||
xml = Entities::SomeRelayable.new(hash).to_xml
|
xml = Entities::SomeRelayable.new(hash).to_xml
|
||||||
|
|
||||||
|
|
@ -450,13 +402,6 @@ module DiasporaFederation
|
||||||
Entities::SomeRelayable.new(hash).to_json
|
Entities::SomeRelayable.new(hash).to_json
|
||||||
}.to raise_error Entities::Relayable::AuthorPrivateKeyNotFound
|
}.to raise_error Entities::Relayable::AuthorPrivateKeyNotFound
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't contain the parent_author_signature" do
|
|
||||||
expect_callback(:fetch_private_key, author).and_return(author_pkey)
|
|
||||||
|
|
||||||
json = Entities::SomeRelayable.new(hash).to_json
|
|
||||||
expect(json[:entity_data]).not_to include(:parent_author_signature)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".from_hash" do
|
describe ".from_hash" do
|
||||||
|
|
@ -471,7 +416,6 @@ module DiasporaFederation
|
||||||
context "when properties are sorted and there is an unknown property" do
|
context "when properties are sorted and there is an unknown property" do
|
||||||
let(:new_signature_data) { "#{author};#{guid};#{parent_guid};#{new_property};#{property}" }
|
let(:new_signature_data) { "#{author};#{guid};#{parent_guid};#{new_property};#{property}" }
|
||||||
let(:author_signature) { sign_with_key(author_pkey, new_signature_data) }
|
let(:author_signature) { sign_with_key(author_pkey, new_signature_data) }
|
||||||
let(:parent_author_signature) { sign_with_key(parent_pkey, new_signature_data) }
|
|
||||||
let(:entity_data) {
|
let(:entity_data) {
|
||||||
{
|
{
|
||||||
:guid => guid,
|
:guid => guid,
|
||||||
|
|
@ -479,8 +423,7 @@ module DiasporaFederation
|
||||||
:property => property,
|
:property => property,
|
||||||
:parent_guid => parent_guid,
|
:parent_guid => parent_guid,
|
||||||
"new_property" => new_property,
|
"new_property" => new_property,
|
||||||
:author_signature => author_signature,
|
:author_signature => author_signature
|
||||||
:parent_author_signature => parent_author_signature
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let(:property_order) { %w[author guid parent_guid new_property property] }
|
let(:property_order) { %w[author guid parent_guid new_property property] }
|
||||||
|
|
@ -493,7 +436,6 @@ module DiasporaFederation
|
||||||
expect(entity.parent_guid).to eq(parent_guid)
|
expect(entity.parent_guid).to eq(parent_guid)
|
||||||
expect(entity.property).to eq(property)
|
expect(entity.property).to eq(property)
|
||||||
expect(entity.author_signature).to eq(author_signature)
|
expect(entity.author_signature).to eq(author_signature)
|
||||||
expect(entity.parent_author_signature).to eq(parent_author_signature)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "makes unknown properties available via #additional_data" do
|
it "makes unknown properties available via #additional_data" do
|
||||||
|
|
@ -514,7 +456,6 @@ module DiasporaFederation
|
||||||
parent_guid: parent_guid,
|
parent_guid: parent_guid,
|
||||||
property: property,
|
property: property,
|
||||||
author_signature: author_signature,
|
author_signature: author_signature,
|
||||||
parent_author_signature: parent_author_signature,
|
|
||||||
parent: remote_parent
|
parent: remote_parent
|
||||||
}.merge("new_property" => new_property),
|
}.merge("new_property" => new_property),
|
||||||
%w[author guid parent_guid new_property property],
|
%w[author guid parent_guid new_property property],
|
||||||
|
|
@ -532,8 +473,7 @@ module DiasporaFederation
|
||||||
author: author,
|
author: author,
|
||||||
property: property,
|
property: property,
|
||||||
parent_guid: parent_guid,
|
parent_guid: parent_guid,
|
||||||
author_signature: sign_with_key(author_pkey, signature_data),
|
author_signature: sign_with_key(author_pkey, signature_data)
|
||||||
parent_author_signature: sign_with_key(parent_pkey, signature_data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entity = Entities::SomeRelayable.from_hash(entity_data, property_order)
|
entity = Entities::SomeRelayable.from_hash(entity_data, property_order)
|
||||||
|
|
@ -551,8 +491,7 @@ module DiasporaFederation
|
||||||
author: author,
|
author: author,
|
||||||
property: property,
|
property: property,
|
||||||
parent_guid: parent_guid,
|
parent_guid: parent_guid,
|
||||||
author_signature: "aa",
|
author_signature: "aa"
|
||||||
parent_author_signature: "bb"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expect_callback(:fetch_related_entity, "Parent", parent_guid).and_return(remote_parent)
|
expect_callback(:fetch_related_entity, "Parent", parent_guid).and_return(remote_parent)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ end
|
||||||
def add_signatures(hash, klass=described_class)
|
def add_signatures(hash, klass=described_class)
|
||||||
properties = klass.new(hash).send(:xml_elements)
|
properties = klass.new(hash).send(:xml_elements)
|
||||||
hash[:author_signature] = properties[:author_signature]
|
hash[:author_signature] = properties[:author_signature]
|
||||||
hash[:parent_author_signature] = properties[:parent_author_signature]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def sign_with_key(privkey, signature_data)
|
def sign_with_key(privkey, signature_data)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
def entity_hash_from(hash)
|
def entity_hash_from(hash)
|
||||||
hash.delete(:parent_author_signature)
|
|
||||||
hash.map {|key, value|
|
hash.map {|key, value|
|
||||||
if [String, TrueClass, FalseClass, Integer, NilClass].any? {|c| value.is_a? c }
|
if [String, TrueClass, FalseClass, Integer, NilClass].any? {|c| value.is_a? c }
|
||||||
[key, value]
|
[key, value]
|
||||||
|
|
@ -101,24 +100,19 @@ shared_examples "an XML Entity" do |ignored_props=[]|
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples "a relayable Entity" do
|
shared_examples "a relayable Entity" do
|
||||||
let(:instance) { described_class.new(data.merge(author_signature: nil, parent_author_signature: nil)) }
|
let(:instance) { described_class.new(data.merge(author_signature: nil)) }
|
||||||
|
|
||||||
context "signatures generation" do
|
context "signatures generation" do
|
||||||
def verify_signature(pubkey, signature, signed_string)
|
def verify_signature(pubkey, signature, signed_string)
|
||||||
pubkey.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(signature), signed_string)
|
pubkey.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(signature), signed_string)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "computes correct signatures for the entity" do
|
it "computes correct author_signature for the entity" do
|
||||||
order = described_class.class_props.keys - %i[author_signature parent_author_signature parent]
|
order = described_class.class_props.keys - %i[author_signature parent]
|
||||||
signed_string = order.map {|name| data[name].is_a?(Time) ? data[name].iso8601 : data[name] }.join(";")
|
signed_string = order.map {|name| data[name].is_a?(Time) ? data[name].iso8601 : data[name] }.join(";")
|
||||||
|
|
||||||
xml = instance.to_xml
|
author_signature = instance.to_xml.at_xpath("author_signature").text
|
||||||
|
|
||||||
author_signature = xml.at_xpath("author_signature").text
|
|
||||||
parent_author_signature = xml.at_xpath("parent_author_signature").text
|
|
||||||
|
|
||||||
expect(verify_signature(alice.public_key, author_signature, signed_string)).to be_truthy
|
expect(verify_signature(alice.public_key, author_signature, signed_string)).to be_truthy
|
||||||
expect(verify_signature(bob.public_key, parent_author_signature, signed_string)).to be_truthy
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -182,7 +176,6 @@ shared_examples "a relayable JSON entity" do
|
||||||
it "matches JSON schema with empty string signatures" do
|
it "matches JSON schema with empty string signatures" do
|
||||||
json = described_class.new(data).to_json
|
json = described_class.new(data).to_json
|
||||||
json[:entity_data][:author_signature] = ""
|
json[:entity_data][:author_signature] = ""
|
||||||
json[:entity_data][:parent_author_signature] = ""
|
|
||||||
expect(json.to_json).to match_json_schema(:entity_schema)
|
expect(json.to_json).to match_json_schema(:entity_schema)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue