Merge pull request #18 from cmrd-senya/retractions-signatures
Retractions signatures support
This commit is contained in:
commit
5e3ffc4c37
15 changed files with 281 additions and 64 deletions
|
|
@ -21,11 +21,12 @@ module DiasporaFederation
|
||||||
fetch_person_for_webfinger
|
fetch_person_for_webfinger
|
||||||
fetch_person_for_hcard
|
fetch_person_for_hcard
|
||||||
save_person_after_webfinger
|
save_person_after_webfinger
|
||||||
fetch_private_key_by_id
|
fetch_private_key_by_diaspora_id
|
||||||
fetch_private_key_by_post_guid
|
fetch_author_private_key_by_entity_guid
|
||||||
fetch_public_key_by_id
|
fetch_public_key_by_diaspora_id
|
||||||
fetch_public_key_by_post_guid
|
fetch_author_public_key_by_entity_guid
|
||||||
post_author_is_local?
|
entity_author_is_local?
|
||||||
|
fetch_entity_author_id_by_guid
|
||||||
)
|
)
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
|
||||||
|
|
@ -52,22 +52,22 @@ module DiasporaFederation
|
||||||
# @param [Hash] data hash with data to verify
|
# @param [Hash] data hash with data to verify
|
||||||
# @raise [SignatureVerificationFailed] if the signature is not valid or no public key is found
|
# @raise [SignatureVerificationFailed] if the signature is not valid or no public key is found
|
||||||
def self.verify_signatures(data)
|
def self.verify_signatures(data)
|
||||||
pkey = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_id, data[:diaspora_id])
|
pkey = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, data[:diaspora_id])
|
||||||
raise SignatureVerificationFailed, "failed to fetch public key for #{data[:diaspora_id]}" if pkey.nil?
|
raise SignatureVerificationFailed, "failed to fetch public key for #{data[:diaspora_id]}" if pkey.nil?
|
||||||
raise SignatureVerificationFailed, "wrong author_signature" unless Signing.verify_signature(
|
raise SignatureVerificationFailed, "wrong author_signature" unless Signing.verify_signature(
|
||||||
data, data[:author_signature], pkey
|
data, data[:author_signature], pkey
|
||||||
)
|
)
|
||||||
|
|
||||||
author_is_local = DiasporaFederation.callbacks.trigger(:post_author_is_local?, data[:parent_guid])
|
author_is_local = DiasporaFederation.callbacks.trigger(:entity_author_is_local?, "Post", data[:parent_guid])
|
||||||
verify_parent_signature(data) unless author_is_local
|
verify_parent_signature(data) unless author_is_local
|
||||||
end
|
end
|
||||||
|
|
||||||
# this happens only on downstream federation
|
# this happens only on downstream federation
|
||||||
# @param [Hash] data hash with data to verify
|
# @param [Hash] data hash with data to verify
|
||||||
def self.verify_parent_signature(data)
|
def self.verify_parent_signature(data)
|
||||||
pkey = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_post_guid, data[:parent_guid])
|
pkey = DiasporaFederation.callbacks.trigger(:fetch_author_public_key_by_entity_guid, "Post", data[:parent_guid])
|
||||||
raise SignatureVerificationFailed,
|
raise SignatureVerificationFailed,
|
||||||
"failed to fetch public key for parent of #{data[:parent_guid]}" if pkey.nil?
|
"failed to fetch public key for author of #{data[:parent_guid]}" if pkey.nil?
|
||||||
raise SignatureVerificationFailed, "wrong parent_author_signature" unless Signing.verify_signature(
|
raise SignatureVerificationFailed, "wrong parent_author_signature" unless Signing.verify_signature(
|
||||||
data, data[:parent_author_signature], pkey
|
data, data[:parent_author_signature], pkey
|
||||||
)
|
)
|
||||||
|
|
@ -80,12 +80,16 @@ module DiasporaFederation
|
||||||
# @param [Hash] data hash given for a signing
|
# @param [Hash] data hash given for a signing
|
||||||
def self.update_signatures!(data)
|
def self.update_signatures!(data)
|
||||||
if data[:author_signature].nil?
|
if data[:author_signature].nil?
|
||||||
pkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_id, data[:diaspora_id])
|
pkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_diaspora_id, data[:diaspora_id])
|
||||||
data[:author_signature] = Signing.sign_with_key(data, pkey) unless pkey.nil?
|
data[:author_signature] = Signing.sign_with_key(data, pkey) unless pkey.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
if data[:parent_author_signature].nil?
|
if data[:parent_author_signature].nil?
|
||||||
pkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_post_guid, data[:parent_guid])
|
pkey = DiasporaFederation.callbacks.trigger(
|
||||||
|
:fetch_author_private_key_by_entity_guid,
|
||||||
|
"Post",
|
||||||
|
data[:parent_guid]
|
||||||
|
)
|
||||||
data[:parent_author_signature] = Signing.sign_with_key(data, pkey) unless pkey.nil?
|
data[:parent_author_signature] = Signing.sign_with_key(data, pkey) unless pkey.nil?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ module DiasporaFederation
|
||||||
# Contains a signature of the entity using the private key of the author of a parent post
|
# Contains a signature of the entity using the private key of the author of a parent post
|
||||||
# This signature is mandatory only when federation from an upstream author to the subscribers.
|
# This signature is mandatory only when federation from an upstream author to the subscribers.
|
||||||
# @return [String] parent author signature
|
# @return [String] parent author signature
|
||||||
property :parent_author_signature
|
property :parent_author_signature, default: nil
|
||||||
|
|
||||||
# @!attribute [r] target_guid
|
# @!attribute [r] target_guid
|
||||||
# guid of a post to be deleted
|
# guid of a post to be deleted
|
||||||
|
|
@ -46,7 +46,45 @@ module DiasporaFederation
|
||||||
# This signature is mandatory only when federation from the subscriber to an upstream
|
# This signature is mandatory only when federation from the subscriber to an upstream
|
||||||
# author is done.
|
# author is done.
|
||||||
# @return [String] target author signature
|
# @return [String] target author signature
|
||||||
property :target_author_signature
|
property :target_author_signature, default: nil
|
||||||
|
|
||||||
|
# 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_h
|
||||||
|
RelayableRetraction.update_signatures!(hash)
|
||||||
|
xml.at_xpath("target_author_signature").content = hash[:target_author_signature]
|
||||||
|
xml.at_xpath("parent_author_signature").content = hash[:parent_author_signature]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adds signatures to a given 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.
|
||||||
|
#
|
||||||
|
# @param [Hash] hash hash given for a signing
|
||||||
|
def self.update_signatures!(hash)
|
||||||
|
target_author = DiasporaFederation.callbacks.trigger(
|
||||||
|
:fetch_entity_author_id_by_guid,
|
||||||
|
hash[:target_type],
|
||||||
|
hash[:target_guid]
|
||||||
|
)
|
||||||
|
pkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
|
|
||||||
|
fill_required_signature(target_author, pkey, hash) unless pkey.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.fill_required_signature(target_author, pkey, hash)
|
||||||
|
if target_author == hash[:diaspora_id] && hash[:target_author_signature].nil?
|
||||||
|
hash[:target_author_signature] =
|
||||||
|
Signing.sign_with_key(SignedRetraction.apply_signable_exceptions(hash), pkey)
|
||||||
|
elsif target_author != hash[:diaspora_id] && hash[:parent_author_signature].nil?
|
||||||
|
hash[:parent_author_signature] =
|
||||||
|
Signing.sign_with_key(SignedRetraction.apply_signable_exceptions(hash), pkey)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private_class_method :fill_required_signature
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,39 @@ module DiasporaFederation
|
||||||
# Contains a signature of the entity using the private key of the author of a post
|
# Contains a signature of the entity using the private key of the author of a post
|
||||||
# This signature is mandatory.
|
# This signature is mandatory.
|
||||||
# @return [String] author signature
|
# @return [String] author signature
|
||||||
property :target_author_signature
|
property :target_author_signature, default: nil
|
||||||
|
|
||||||
|
# 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_h
|
||||||
|
SignedRetraction.update_signatures!(hash)
|
||||||
|
|
||||||
|
xml.at_xpath("target_author_signature").content = hash[:target_author_signature]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adds signature to a given hash with the key of the author
|
||||||
|
# if the signature is not in the hash yet and if the key is available.
|
||||||
|
#
|
||||||
|
# @param [Hash] data hash given for a signing
|
||||||
|
def self.update_signatures!(data)
|
||||||
|
if data[:target_author_signature].nil?
|
||||||
|
pkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_diaspora_id, data[:diaspora_id])
|
||||||
|
data[:target_author_signature] = Signing.sign_with_key(apply_signable_exceptions(data), pkey) unless pkey.nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Deletes :diaspora_id (xml_name: sender_handle) from the hash in order to compute
|
||||||
|
# a signature since it is included from signable_string for SignedRetraction and RelayableRetraction
|
||||||
|
#
|
||||||
|
# @param [Hash] data hash of the retraction properties
|
||||||
|
# @retrun [Hash] hash copy without :diaspora_id member
|
||||||
|
def self.apply_signable_exceptions(data)
|
||||||
|
data.dup.tap {|data| data.delete(:diaspora_id) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -26,5 +26,29 @@ module DiasporaFederation
|
||||||
DiasporaFederation::Entities::Relayable.update_signatures!(data)
|
DiasporaFederation::Entities::Relayable.update_signatures!(data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Generates attributes for signed retraction entity constructor with correct signatures in it
|
||||||
|
#
|
||||||
|
# @return [Hash] hash with correct signatures
|
||||||
|
def self.signed_retraction_attributes_with_signatures
|
||||||
|
sort_hash(FactoryGirl.attributes_for(:signed_retraction_entity), Entities::SignedRetraction).tap do |data|
|
||||||
|
Entities::SignedRetraction.update_signatures!(data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generates attributes for relayable retraction entity constructor with correct signatures in it
|
||||||
|
#
|
||||||
|
# @return [Hash] hash with correct signatures
|
||||||
|
def self.relayable_retraction_attributes_with_signatures
|
||||||
|
sort_hash(
|
||||||
|
FactoryGirl.attributes_for(
|
||||||
|
:relayable_retraction_entity,
|
||||||
|
target_author_signature: "false sig"
|
||||||
|
),
|
||||||
|
Entities::RelayableRetraction
|
||||||
|
).tap do |data|
|
||||||
|
Entities::RelayableRetraction.update_signatures!(data)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,6 @@ FactoryGirl.define do
|
||||||
sequence(:guid) { UUID.generate :compact }
|
sequence(:guid) { UUID.generate :compact }
|
||||||
sequence(:diaspora_id) {|n| "person-#{n}-#{SecureRandom.hex(3)}@localhost:3000" }
|
sequence(:diaspora_id) {|n| "person-#{n}-#{SecureRandom.hex(3)}@localhost:3000" }
|
||||||
sequence(:public_key) { OpenSSL::PKey::RSA.generate(1024).public_key.export }
|
sequence(:public_key) { OpenSSL::PKey::RSA.generate(1024).public_key.export }
|
||||||
sequence(:signature) do |i|
|
|
||||||
abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
ltr = abc[i % abc.length]
|
|
||||||
"#{ltr * 6}=="
|
|
||||||
end
|
|
||||||
|
|
||||||
factory :webfinger, class: DiasporaFederation::Discovery::WebFinger do
|
factory :webfinger, class: DiasporaFederation::Discovery::WebFinger do
|
||||||
guid
|
guid
|
||||||
|
|
@ -140,11 +135,9 @@ FactoryGirl.define do
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :relayable_retraction_entity, class: DiasporaFederation::Entities::RelayableRetraction do
|
factory :relayable_retraction_entity, class: DiasporaFederation::Entities::RelayableRetraction do
|
||||||
parent_author_signature { generate(:signature) }
|
|
||||||
target_guid { generate(:guid) }
|
target_guid { generate(:guid) }
|
||||||
target_type "Post"
|
target_type "Comment"
|
||||||
diaspora_id
|
diaspora_id
|
||||||
target_author_signature { generate(:signature) }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :reshare_entity, class: DiasporaFederation::Entities::Reshare do
|
factory :reshare_entity, class: DiasporaFederation::Entities::Reshare do
|
||||||
|
|
@ -167,7 +160,6 @@ FactoryGirl.define do
|
||||||
target_guid { generate(:guid) }
|
target_guid { generate(:guid) }
|
||||||
target_type "Post"
|
target_type "Post"
|
||||||
diaspora_id
|
diaspora_id
|
||||||
target_author_signature { generate(:signature) }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :poll_answer_entity, class: DiasporaFederation::Entities::PollAnswer do
|
factory :poll_answer_entity, class: DiasporaFederation::Entities::PollAnswer do
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,11 @@ module DiasporaFederation
|
||||||
class RelayableRetractionValidator < Validation::Validator
|
class RelayableRetractionValidator < Validation::Validator
|
||||||
include Validation
|
include Validation
|
||||||
|
|
||||||
rule :parent_author_signature, :not_empty
|
|
||||||
|
|
||||||
rule :target_guid, :guid
|
rule :target_guid, :guid
|
||||||
|
|
||||||
rule :target_type, :not_empty
|
rule :target_type, :not_empty
|
||||||
|
|
||||||
rule :diaspora_id, %i(not_empty diaspora_id)
|
rule :diaspora_id, %i(not_empty diaspora_id)
|
||||||
|
|
||||||
rule :target_author_signature, :not_empty
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ module DiasporaFederation
|
||||||
rule :target_type, :not_empty
|
rule :target_type, :not_empty
|
||||||
|
|
||||||
rule :diaspora_id, %i(not_empty diaspora_id)
|
rule :diaspora_id, %i(not_empty diaspora_id)
|
||||||
|
|
||||||
rule :target_author_signature, :not_empty
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
module DiasporaFederation
|
module DiasporaFederation
|
||||||
describe Entities::RelayableRetraction do
|
describe Entities::RelayableRetraction do
|
||||||
let(:data) { FactoryGirl.attributes_for(:relayable_retraction_entity) }
|
let(:data) { Test.relayable_retraction_attributes_with_signatures }
|
||||||
|
|
||||||
let(:xml) {
|
let(:xml) {
|
||||||
<<-XML
|
<<-XML
|
||||||
|
|
@ -17,5 +17,70 @@ XML
|
||||||
it_behaves_like "an Entity subclass"
|
it_behaves_like "an Entity subclass"
|
||||||
|
|
||||||
it_behaves_like "an XML Entity"
|
it_behaves_like "an XML Entity"
|
||||||
|
|
||||||
|
describe ".update_singatures!" do
|
||||||
|
let(:author_pkey) { OpenSSL::PKey::RSA.generate(1024) }
|
||||||
|
let(:hash) { FactoryGirl.attributes_for(:relayable_retraction_entity) }
|
||||||
|
|
||||||
|
it "updates author signature when it was nil and key was supplied" do
|
||||||
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(
|
||||||
|
:fetch_entity_author_id_by_guid,
|
||||||
|
hash[:target_type],
|
||||||
|
hash[:target_guid]
|
||||||
|
)
|
||||||
|
.and_return(hash[:diaspora_id])
|
||||||
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_private_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
|
.and_return(author_pkey)
|
||||||
|
|
||||||
|
Entities::RelayableRetraction.update_signatures!(hash)
|
||||||
|
|
||||||
|
signable_hash = hash.select do |key, _|
|
||||||
|
%i(target_guid target_type).include?(key)
|
||||||
|
end
|
||||||
|
expect(Signing.verify_signature(signable_hash, hash[:target_author_signature], author_pkey)).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates parent author signature when it was nil, key was supplied and sender is not author of the target" do
|
||||||
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(
|
||||||
|
:fetch_entity_author_id_by_guid,
|
||||||
|
hash[:target_type],
|
||||||
|
hash[:target_guid]
|
||||||
|
)
|
||||||
|
.and_return(FactoryGirl.generate(:diaspora_id))
|
||||||
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_private_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
|
.and_return(author_pkey)
|
||||||
|
|
||||||
|
Entities::RelayableRetraction.update_signatures!(hash)
|
||||||
|
|
||||||
|
signable_hash = hash.select do |key, _|
|
||||||
|
%i(target_guid target_type).include?(key)
|
||||||
|
end
|
||||||
|
expect(Signing.verify_signature(signable_hash, hash[:parent_author_signature], author_pkey)).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't change signatures if they are already set" do
|
||||||
|
signatures = {target_author_signature: "aa"}
|
||||||
|
hash.merge!(signatures)
|
||||||
|
|
||||||
|
Entities::RelayableRetraction.update_signatures!(hash)
|
||||||
|
expect(hash[:target_author_signature]).to eq(signatures[:target_author_signature])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't change signatures if keys weren't supplied" do
|
||||||
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_private_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
|
.and_return(nil)
|
||||||
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_entity_author_id_by_guid, "Comment", hash[:target_guid])
|
||||||
|
.and_return(hash[:diaspora_id])
|
||||||
|
|
||||||
|
Entities::RelayableRetraction.update_signatures!(hash)
|
||||||
|
expect(hash[:target_author_signature]).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -15,18 +15,24 @@ module DiasporaFederation
|
||||||
hash[:author_signature] = Signing.sign_with_key(hash, author_pkey)
|
hash[:author_signature] = Signing.sign_with_key(hash, author_pkey)
|
||||||
hash[:parent_author_signature] = Signing.sign_with_key(hash, parent_pkey)
|
hash[:parent_author_signature] = Signing.sign_with_key(hash, parent_pkey)
|
||||||
|
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:fetch_public_key_by_id, hash[:diaspora_id])
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_public_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
.and_return(author_pkey.public_key)
|
.and_return(author_pkey.public_key)
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
.with(:fetch_public_key_by_post_guid, hash[:parent_guid])
|
.with(
|
||||||
|
:fetch_author_public_key_by_entity_guid,
|
||||||
|
"Post",
|
||||||
|
hash[:parent_guid]
|
||||||
|
)
|
||||||
.and_return(parent_pkey.public_key)
|
.and_return(parent_pkey.public_key)
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:post_author_is_local?, hash[:parent_guid])
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:entity_author_is_local?, "Post", hash[:parent_guid])
|
||||||
.and_return(false)
|
.and_return(false)
|
||||||
expect { Entities::Relayable.verify_signatures(hash) }.not_to raise_error
|
expect { Entities::Relayable.verify_signatures(hash) }.not_to raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises when no public key for author was fetched" do
|
it "raises when no public key for author was fetched" do
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:fetch_public_key_by_id, anything)
|
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:fetch_public_key_by_diaspora_id, anything)
|
||||||
.and_return(nil)
|
.and_return(nil)
|
||||||
|
|
||||||
expect { Entities::Relayable.verify_signatures(hash) }.to raise_error(
|
expect { Entities::Relayable.verify_signatures(hash) }.to raise_error(
|
||||||
|
|
@ -37,7 +43,8 @@ module DiasporaFederation
|
||||||
it "raises when bad author signature was passed" do
|
it "raises when bad author signature was passed" do
|
||||||
hash[:author_signature] = nil
|
hash[:author_signature] = nil
|
||||||
|
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:fetch_public_key_by_id, hash[:diaspora_id])
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_public_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
.and_return(author_pkey.public_key)
|
.and_return(author_pkey.public_key)
|
||||||
expect { Entities::Relayable.verify_signatures(hash) }.to raise_error(
|
expect { Entities::Relayable.verify_signatures(hash) }.to raise_error(
|
||||||
Entities::Relayable::SignatureVerificationFailed
|
Entities::Relayable::SignatureVerificationFailed
|
||||||
|
|
@ -47,12 +54,18 @@ module DiasporaFederation
|
||||||
it "raises when no public key for parent author was fetched" do
|
it "raises when no public key for parent author was fetched" do
|
||||||
hash[:author_signature] = Signing.sign_with_key(hash, author_pkey)
|
hash[:author_signature] = Signing.sign_with_key(hash, author_pkey)
|
||||||
|
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:fetch_public_key_by_id, hash[:diaspora_id])
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_public_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
.and_return(author_pkey.public_key)
|
.and_return(author_pkey.public_key)
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
.with(:fetch_public_key_by_post_guid, hash[:parent_guid])
|
.with(
|
||||||
|
:fetch_author_public_key_by_entity_guid,
|
||||||
|
"Post",
|
||||||
|
hash[:parent_guid]
|
||||||
|
)
|
||||||
.and_return(nil)
|
.and_return(nil)
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:post_author_is_local?, hash[:parent_guid])
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:entity_author_is_local?, "Post", hash[:parent_guid])
|
||||||
.and_return(false)
|
.and_return(false)
|
||||||
expect { Entities::Relayable.verify_signatures(hash) }.to raise_error(
|
expect { Entities::Relayable.verify_signatures(hash) }.to raise_error(
|
||||||
Entities::Relayable::SignatureVerificationFailed
|
Entities::Relayable::SignatureVerificationFailed
|
||||||
|
|
@ -63,12 +76,18 @@ module DiasporaFederation
|
||||||
hash[:author_signature] = Signing.sign_with_key(hash, author_pkey)
|
hash[:author_signature] = Signing.sign_with_key(hash, author_pkey)
|
||||||
hash[:parent_author_signature] = nil
|
hash[:parent_author_signature] = nil
|
||||||
|
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:fetch_public_key_by_id, hash[:diaspora_id])
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_public_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
.and_return(author_pkey.public_key)
|
.and_return(author_pkey.public_key)
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
.with(:fetch_public_key_by_post_guid, hash[:parent_guid])
|
.with(
|
||||||
|
:fetch_author_public_key_by_entity_guid,
|
||||||
|
"Post",
|
||||||
|
hash[:parent_guid]
|
||||||
|
)
|
||||||
.and_return(parent_pkey.public_key)
|
.and_return(parent_pkey.public_key)
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:post_author_is_local?, hash[:parent_guid])
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:entity_author_is_local?, "Post", hash[:parent_guid])
|
||||||
.and_return(false)
|
.and_return(false)
|
||||||
expect { Entities::Relayable.verify_signatures(hash) }.to raise_error(
|
expect { Entities::Relayable.verify_signatures(hash) }.to raise_error(
|
||||||
Entities::Relayable::SignatureVerificationFailed
|
Entities::Relayable::SignatureVerificationFailed
|
||||||
|
|
@ -79,9 +98,11 @@ module DiasporaFederation
|
||||||
hash[:author_signature] = Signing.sign_with_key(hash, author_pkey)
|
hash[:author_signature] = Signing.sign_with_key(hash, author_pkey)
|
||||||
hash[:parent_author_signature] = nil
|
hash[:parent_author_signature] = nil
|
||||||
|
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:fetch_public_key_by_id, hash[:diaspora_id])
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_public_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
.and_return(author_pkey.public_key)
|
.and_return(author_pkey.public_key)
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:post_author_is_local?, hash[:parent_guid])
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:entity_author_is_local?, "Post", hash[:parent_guid])
|
||||||
.and_return(true)
|
.and_return(true)
|
||||||
expect { Entities::Relayable.verify_signatures(hash) }.not_to raise_error
|
expect { Entities::Relayable.verify_signatures(hash) }.not_to raise_error
|
||||||
end
|
end
|
||||||
|
|
@ -89,10 +110,15 @@ module DiasporaFederation
|
||||||
|
|
||||||
describe ".update_singatures!" do
|
describe ".update_singatures!" do
|
||||||
it "updates signatures when they were nil and keys were supplied" do
|
it "updates signatures when they were nil and keys were supplied" do
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:fetch_private_key_by_id, hash[:diaspora_id])
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_private_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
.and_return(author_pkey)
|
.and_return(author_pkey)
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
.with(:fetch_private_key_by_post_guid, hash[:parent_guid])
|
.with(
|
||||||
|
:fetch_author_private_key_by_entity_guid,
|
||||||
|
"Post",
|
||||||
|
hash[:parent_guid]
|
||||||
|
)
|
||||||
.and_return(parent_pkey)
|
.and_return(parent_pkey)
|
||||||
|
|
||||||
Entities::Relayable.update_signatures!(hash)
|
Entities::Relayable.update_signatures!(hash)
|
||||||
|
|
@ -110,10 +136,15 @@ module DiasporaFederation
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't change signatures if keys weren't supplied" do
|
it "doesn't change signatures if keys weren't supplied" do
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:fetch_private_key_by_id, hash[:diaspora_id])
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_private_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
.and_return(nil)
|
.and_return(nil)
|
||||||
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
.with(:fetch_private_key_by_post_guid, hash[:parent_guid])
|
.with(
|
||||||
|
:fetch_author_private_key_by_entity_guid,
|
||||||
|
"Post",
|
||||||
|
hash[:parent_guid]
|
||||||
|
)
|
||||||
.and_return(nil)
|
.and_return(nil)
|
||||||
|
|
||||||
Entities::Relayable.update_signatures!(hash)
|
Entities::Relayable.update_signatures!(hash)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
module DiasporaFederation
|
module DiasporaFederation
|
||||||
describe Entities::SignedRetraction do
|
describe Entities::SignedRetraction do
|
||||||
let(:data) { FactoryGirl.attributes_for(:signed_retraction_entity) }
|
let(:data) { Test.signed_retraction_attributes_with_signatures }
|
||||||
|
|
||||||
let(:xml) {
|
let(:xml) {
|
||||||
<<-XML
|
<<-XML
|
||||||
|
|
@ -16,5 +16,41 @@ XML
|
||||||
it_behaves_like "an Entity subclass"
|
it_behaves_like "an Entity subclass"
|
||||||
|
|
||||||
it_behaves_like "an XML Entity"
|
it_behaves_like "an XML Entity"
|
||||||
|
|
||||||
|
describe ".update_singatures!" do
|
||||||
|
let(:author_pkey) { OpenSSL::PKey::RSA.generate(1024) }
|
||||||
|
let(:hash) { FactoryGirl.attributes_for(:signed_retraction_entity) }
|
||||||
|
|
||||||
|
it "updates author signature when it was nil and key was supplied" do
|
||||||
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_private_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
|
.and_return(author_pkey)
|
||||||
|
|
||||||
|
signable_hash = hash.select do |key, _|
|
||||||
|
%i(target_guid target_type).include?(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
Entities::SignedRetraction.update_signatures!(hash)
|
||||||
|
|
||||||
|
expect(Signing.verify_signature(signable_hash, hash[:target_author_signature], author_pkey)).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't change signature if it is already set" do
|
||||||
|
signatures = {target_author_signature: "aa"}
|
||||||
|
hash.merge!(signatures)
|
||||||
|
|
||||||
|
Entities::SignedRetraction.update_signatures!(hash)
|
||||||
|
expect(hash[:target_author_signature]).to eq(signatures[:target_author_signature])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't change signature if a key wasn't supplied" do
|
||||||
|
expect(DiasporaFederation.callbacks).to receive(:trigger)
|
||||||
|
.with(:fetch_private_key_by_diaspora_id, hash[:diaspora_id])
|
||||||
|
.and_return(nil)
|
||||||
|
|
||||||
|
Entities::SignedRetraction.update_signatures!(hash)
|
||||||
|
expect(hash[:author_signature]).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,9 @@ module DiasporaFederation
|
||||||
let(:property) { :target_guid }
|
let(:property) { :target_guid }
|
||||||
end
|
end
|
||||||
|
|
||||||
%i(parent_author_signature target_author_signature).each do |prop|
|
describe "#target_type" do
|
||||||
describe "##{prop}" do
|
|
||||||
it_behaves_like "a property that mustn't be empty" do
|
it_behaves_like "a property that mustn't be empty" do
|
||||||
let(:property) { prop }
|
let(:property) { :target_type }
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,9 @@ module DiasporaFederation
|
||||||
let(:property) { :target_guid }
|
let(:property) { :target_guid }
|
||||||
end
|
end
|
||||||
|
|
||||||
%i(target_type target_author_signature).each do |prop|
|
describe "#target_type" do
|
||||||
describe "##{prop}" do
|
|
||||||
it_behaves_like "a property that mustn't be empty" do
|
it_behaves_like "a property that mustn't be empty" do
|
||||||
let(:property) { prop }
|
let(:property) { :target_type }
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ def alice
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_pkey
|
def test_pkey
|
||||||
DiasporaFederation.callbacks.trigger(:fetch_private_key_by_id)
|
DiasporaFederation.callbacks.trigger(:fetch_private_key_by_diaspora_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Requires supporting files with custom matchers and macros, etc,
|
# Requires supporting files with custom matchers and macros, etc,
|
||||||
|
|
|
||||||
|
|
@ -63,24 +63,28 @@ DiasporaFederation.configure do |config|
|
||||||
@test_pkey ||= OpenSSL::PKey::RSA.generate(1024)
|
@test_pkey ||= OpenSSL::PKey::RSA.generate(1024)
|
||||||
end
|
end
|
||||||
|
|
||||||
on :fetch_private_key_by_id do
|
on :fetch_private_key_by_diaspora_id do
|
||||||
pkey
|
pkey
|
||||||
end
|
end
|
||||||
|
|
||||||
on :fetch_private_key_by_post_guid do
|
on :fetch_author_private_key_by_entity_guid do
|
||||||
pkey
|
pkey
|
||||||
end
|
end
|
||||||
|
|
||||||
on :fetch_public_key_by_id do
|
on :fetch_public_key_by_diaspora_id do
|
||||||
pkey.public_key
|
pkey.public_key
|
||||||
end
|
end
|
||||||
|
|
||||||
on :fetch_public_key_by_post_guid do
|
on :fetch_author_public_key_by_entity_guid do
|
||||||
pkey.public_key
|
pkey.public_key
|
||||||
end
|
end
|
||||||
|
|
||||||
on :post_author_is_local? do
|
on :entity_author_is_local? do
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
on :fetch_entity_author_id_by_guid do
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue