Remove sign-code from SignedRetraction and RelayableRetraction
Second step of #27
This commit is contained in:
parent
e5b2ef71e8
commit
cd3a7abf4d
12 changed files with 44 additions and 355 deletions
|
|
@ -53,50 +53,13 @@ module DiasporaFederation
|
|||
# @return [String] target author signature
|
||||
property :target_author_signature, :string, default: nil
|
||||
|
||||
# @!attribute [r] target
|
||||
# Target entity
|
||||
# @return [RelatedEntity] target entity
|
||||
entity :target, Entities::RelatedEntity
|
||||
|
||||
# Use only {Retraction} for receive
|
||||
# @return [Retraction] instance as normal retraction
|
||||
def to_retraction
|
||||
Retraction.new(author: author, target_guid: target_guid, target_type: target_type, target: target)
|
||||
end
|
||||
|
||||
# @return [String] string representation of this object
|
||||
def to_s
|
||||
"RelayableRetraction:#{target_type}:#{target_guid}"
|
||||
def initialize(*)
|
||||
raise "Sending RelayableRetraction is not supported anymore! Use Retraction instead!"
|
||||
end
|
||||
|
||||
# @return [Retraction] instance
|
||||
def self.from_hash(hash)
|
||||
hash[:target] = Retraction.send(:fetch_target, hash[:target_type], hash[:target_guid])
|
||||
new(hash).to_retraction
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# 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] xml elements with updated signatures
|
||||
def enriched_properties
|
||||
privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key, author)
|
||||
|
||||
super.tap do |hash|
|
||||
fill_required_signature(privkey, hash) unless privkey.nil?
|
||||
end
|
||||
end
|
||||
|
||||
# @param [OpenSSL::PKey::RSA] privkey private key of sender
|
||||
# @param [Hash] hash hash given for a signing
|
||||
def fill_required_signature(privkey, hash)
|
||||
if target.parent.author == author && parent_author_signature.nil?
|
||||
hash[:parent_author_signature] = SignedRetraction.sign_with_key(privkey, self)
|
||||
elsif target.author == author && target_author_signature.nil?
|
||||
hash[:target_author_signature] = SignedRetraction.sign_with_key(privkey, self)
|
||||
end
|
||||
Retraction.from_hash(hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -30,51 +30,13 @@ module DiasporaFederation
|
|||
# @return [String] author signature
|
||||
property :target_author_signature, :string, default: nil
|
||||
|
||||
# @!attribute [r] target
|
||||
# Target entity
|
||||
# @return [RelatedEntity] target entity
|
||||
entity :target, Entities::RelatedEntity
|
||||
|
||||
# Use only {Retraction} for receive
|
||||
# @return [Retraction] instance as normal retraction
|
||||
def to_retraction
|
||||
Retraction.new(author: author, target_guid: target_guid, target_type: target_type, target: target)
|
||||
end
|
||||
|
||||
# Create signature for a retraction
|
||||
# @param [OpenSSL::PKey::RSA] privkey private key of sender
|
||||
# @param [SignedRetraction, RelayableRetraction] ret the retraction to sign
|
||||
# @return [String] a Base64 encoded signature of the retraction with the key
|
||||
def self.sign_with_key(privkey, ret)
|
||||
Base64.strict_encode64(privkey.sign(Relayable::DIGEST, [ret.target_guid, ret.target_type].join(";")))
|
||||
end
|
||||
|
||||
# @return [String] string representation of this object
|
||||
def to_s
|
||||
"SignedRetraction:#{target_type}:#{target_guid}"
|
||||
def initialize(*)
|
||||
raise "Sending SignedRetraction is not supported anymore! Use Retraction instead!"
|
||||
end
|
||||
|
||||
# @return [Retraction] instance
|
||||
def self.from_hash(hash)
|
||||
hash[:target] = Retraction.send(:fetch_target, hash[:target_type], hash[:target_guid])
|
||||
new(hash).to_retraction
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# 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] xml elements with updated signatures
|
||||
def enriched_properties
|
||||
super.tap do |hash|
|
||||
hash[:target_author_signature] = target_author_signature || sign_with_author.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def sign_with_author
|
||||
privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key, author)
|
||||
SignedRetraction.sign_with_key(privkey, self) unless privkey.nil?
|
||||
Retraction.from_hash(hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -150,13 +150,6 @@ module DiasporaFederation
|
|||
conversation_guid { Fabricate.sequence(:guid) }
|
||||
end
|
||||
|
||||
Fabricator(:relayable_retraction_entity, class_name: DiasporaFederation::Entities::RelayableRetraction) do
|
||||
author { Fabricate.sequence(:diaspora_id) }
|
||||
target_guid { Fabricate.sequence(:guid) }
|
||||
target_type "Comment"
|
||||
target {|attrs| Fabricate(:related_entity, author: attrs[:author]) }
|
||||
end
|
||||
|
||||
Fabricator(:reshare_entity, class_name: DiasporaFederation::Entities::Reshare) do
|
||||
root_author { Fabricate.sequence(:diaspora_id) }
|
||||
root_guid { Fabricate.sequence(:guid) }
|
||||
|
|
@ -174,13 +167,6 @@ module DiasporaFederation
|
|||
target {|attrs| Fabricate(:related_entity, author: attrs[:author]) }
|
||||
end
|
||||
|
||||
Fabricator(:signed_retraction_entity, class_name: DiasporaFederation::Entities::SignedRetraction) do
|
||||
author { Fabricate.sequence(:diaspora_id) }
|
||||
target_guid { Fabricate.sequence(:guid) }
|
||||
target_type "Post"
|
||||
target {|attrs| Fabricate(:related_entity, author: attrs[:author]) }
|
||||
end
|
||||
|
||||
Fabricator(:poll_answer_entity, class_name: DiasporaFederation::Entities::PollAnswer) do
|
||||
guid { Fabricate.sequence(:guid) }
|
||||
answer { "Obama is a bicycle" }
|
||||
|
|
|
|||
|
|
@ -64,6 +64,4 @@ require "diaspora_federation/validators/status_message_validator"
|
|||
require "diaspora_federation/validators/web_finger_validator"
|
||||
|
||||
# deprecated
|
||||
require "diaspora_federation/validators/relayable_retraction_validator"
|
||||
require "diaspora_federation/validators/request_validator"
|
||||
require "diaspora_federation/validators/signed_retraction_validator"
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
module DiasporaFederation
|
||||
module Validators
|
||||
# This validates a {Entities::RelayableRetraction}.
|
||||
# @deprecated The {Entities::RelayableRetraction} will be replaced with {Entities::Retraction}.
|
||||
class RelayableRetractionValidator < Validation::Validator
|
||||
include Validation
|
||||
|
||||
rule :target_guid, :guid
|
||||
rule :target_type, :not_empty
|
||||
rule :target, :not_nil
|
||||
|
||||
rule :author, %i(not_empty diaspora_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
module DiasporaFederation
|
||||
module Validators
|
||||
# This validates a {Entities::SignedRetraction}.
|
||||
# @deprecated The {Entities::RelayableRetraction} will be replaced with {Entities::Retraction}.
|
||||
class SignedRetractionValidator < Validation::Validator
|
||||
include Validation
|
||||
|
||||
rule :target_guid, :guid
|
||||
rule :target_type, :not_empty
|
||||
rule :target, :not_nil
|
||||
|
||||
rule :author, %i(not_empty diaspora_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -8,117 +8,36 @@ module DiasporaFederation
|
|||
parent: Fabricate(:related_entity, author: alice.diaspora_id)
|
||||
)
|
||||
}
|
||||
let(:data) {
|
||||
Fabricate(
|
||||
:relayable_retraction_entity,
|
||||
author: alice.diaspora_id,
|
||||
target_guid: target.guid,
|
||||
target_type: target.entity_type,
|
||||
target: target_entity
|
||||
).send(:enriched_properties).tap do |data|
|
||||
data[:target_author_signature] = ""
|
||||
data[:target] = target_entity
|
||||
end
|
||||
}
|
||||
let(:data) { {author: alice.diaspora_id, target_guid: target.guid, target_type: target.entity_type} }
|
||||
|
||||
let(:xml) { <<-XML }
|
||||
<relayable_retraction>
|
||||
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
|
||||
<parent_author_signature/>
|
||||
<target_guid>#{data[:target_guid]}</target_guid>
|
||||
<target_type>#{data[:target_type]}</target_type>
|
||||
<author>#{data[:author]}</author>
|
||||
<sender_handle>#{data[:author]}</sender_handle>
|
||||
<target_author_signature/>
|
||||
</relayable_retraction>
|
||||
XML
|
||||
|
||||
let(:string) { "RelayableRetraction:#{data[:target_type]}:#{data[:target_guid]}" }
|
||||
|
||||
it_behaves_like "an Entity subclass"
|
||||
|
||||
it_behaves_like "an XML Entity", %i(parent_author_signature target_author_signature target)
|
||||
|
||||
it_behaves_like "a retraction"
|
||||
|
||||
describe "#to_xml" do
|
||||
let(:author_pkey) { OpenSSL::PKey::RSA.generate(1024) }
|
||||
let(:hash) { Fabricate.attributes_for(:relayable_retraction_entity) }
|
||||
|
||||
it "updates author signature when it was nil and key was supplied and author is not parent author" do
|
||||
parent = Fabricate(:related_entity, author: bob.diaspora_id)
|
||||
hash[:target] = Fabricate(:related_entity, author: hash[:author], parent: parent)
|
||||
|
||||
expect_callback(:fetch_private_key, hash[:author]).and_return(author_pkey)
|
||||
|
||||
signed_string = "#{hash[:target_guid]};#{hash[:target_type]}"
|
||||
|
||||
xml = Entities::RelayableRetraction.new(hash).to_xml
|
||||
|
||||
signature = Base64.decode64(xml.at_xpath("target_author_signature").text)
|
||||
expect(author_pkey.verify(OpenSSL::Digest::SHA256.new, signature, signed_string)).to be_truthy
|
||||
end
|
||||
|
||||
it "sets parent author signature when author is parent author" do
|
||||
parent = Fabricate(:related_entity, author: hash[:author])
|
||||
hash[:target] = Fabricate(:related_entity, author: hash[:author], parent: parent)
|
||||
|
||||
expect_callback(:fetch_private_key, hash[:author]).and_return(author_pkey)
|
||||
|
||||
signed_string = "#{hash[:target_guid]};#{hash[:target_type]}"
|
||||
|
||||
xml = Entities::RelayableRetraction.new(hash).to_xml
|
||||
|
||||
signature = Base64.decode64(xml.at_xpath("parent_author_signature").text)
|
||||
expect(author_pkey.verify(OpenSSL::Digest::SHA256.new, signature, signed_string)).to be_truthy
|
||||
end
|
||||
|
||||
it "updates parent author signature when it was nil, key was supplied and sender is author of the parent" do
|
||||
parent = Fabricate(:related_entity, author: hash[:author])
|
||||
hash[:target] = Fabricate(:related_entity, author: bob.diaspora_id, parent: parent)
|
||||
|
||||
expect_callback(:fetch_private_key, hash[:author]).and_return(author_pkey)
|
||||
|
||||
signed_string = "#{hash[:target_guid]};#{hash[:target_type]}"
|
||||
|
||||
xml = Entities::RelayableRetraction.new(hash).to_xml
|
||||
|
||||
signature = Base64.decode64(xml.at_xpath("parent_author_signature").text)
|
||||
expect(author_pkey.verify(OpenSSL::Digest::SHA256.new, signature, signed_string)).to be_truthy
|
||||
end
|
||||
|
||||
it "doesn't change signatures if they are already set" do
|
||||
hash.merge!(target_author_signature: "aa", parent_author_signature: "bb")
|
||||
|
||||
xml = Entities::RelayableRetraction.new(hash).to_xml
|
||||
|
||||
expect(xml.at_xpath("target_author_signature").text).to eq("aa")
|
||||
expect(xml.at_xpath("parent_author_signature").text).to eq("bb")
|
||||
end
|
||||
|
||||
it "doesn't change signatures if keys weren't supplied" do
|
||||
expect_callback(:fetch_private_key, hash[:author]).and_return(nil)
|
||||
|
||||
xml = Entities::RelayableRetraction.new(hash).to_xml
|
||||
expect(xml.at_xpath("target_author_signature").text).to eq("")
|
||||
expect(xml.at_xpath("parent_author_signature").text).to eq("")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_retraction" do
|
||||
it "copies the attributes to a Retraction" do
|
||||
relayable_retraction = Fabricate(:relayable_retraction_entity)
|
||||
retraction = relayable_retraction.to_retraction
|
||||
|
||||
expect(retraction).to be_a(Entities::Retraction)
|
||||
expect(retraction.author).to eq(relayable_retraction.author)
|
||||
expect(retraction.target_guid).to eq(relayable_retraction.target_guid)
|
||||
expect(retraction.target_type).to eq(relayable_retraction.target_type)
|
||||
describe "#initialize" do
|
||||
it "raises because it is not supported anymore" do
|
||||
expect {
|
||||
Entities::RelayableRetraction.new(data)
|
||||
}.to raise_error RuntimeError,
|
||||
"Sending RelayableRetraction is not supported anymore! Use Retraction instead!"
|
||||
end
|
||||
end
|
||||
|
||||
context "parse retraction" do
|
||||
it "parses the xml as a retraction" do
|
||||
expect(Entities::Retraction).to receive(:fetch_target).and_return(target_entity)
|
||||
retraction = Entities::RelayableRetraction.from_xml(Nokogiri::XML::Document.parse(xml).root)
|
||||
expect(retraction).to be_a(Entities::Retraction)
|
||||
expect(retraction.author).to eq(data[:author])
|
||||
expect(retraction.target_guid).to eq(data[:target_guid])
|
||||
expect(retraction.target_type).to eq(data[:target_type])
|
||||
expect(retraction.target).to eq(target_entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -25,7 +25,16 @@ XML
|
|||
|
||||
it_behaves_like "an XML Entity"
|
||||
|
||||
it_behaves_like "a retraction"
|
||||
context "receive with no target found" do
|
||||
it "raises when no target is found" do
|
||||
unknown_guid = Fabricate.sequence(:guid)
|
||||
retraction = Entities::Retraction.new(data.merge(target_guid: unknown_guid))
|
||||
expect {
|
||||
described_class.from_xml(retraction.to_xml)
|
||||
}.to raise_error DiasporaFederation::Entities::Retraction::TargetNotFound,
|
||||
"not found: #{data[:target_type]}:#{unknown_guid}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#sender_valid?" do
|
||||
context "unrelayable target" do
|
||||
|
|
|
|||
|
|
@ -2,80 +2,35 @@ module DiasporaFederation
|
|||
describe Entities::SignedRetraction do
|
||||
let(:target) { Fabricate(:post, author: alice) }
|
||||
let(:target_entity) { Fabricate(:related_entity, author: alice.diaspora_id) }
|
||||
let(:data) {
|
||||
Fabricate(
|
||||
:signed_retraction_entity,
|
||||
author: alice.diaspora_id,
|
||||
target_guid: target.guid,
|
||||
target_type: target.entity_type,
|
||||
target: target_entity
|
||||
).send(:enriched_properties).merge(target: target_entity)
|
||||
}
|
||||
let(:data) { {author: alice.diaspora_id, target_guid: target.guid, target_type: target.entity_type} }
|
||||
|
||||
let(:xml) { <<-XML }
|
||||
<signed_retraction>
|
||||
<target_guid>#{data[:target_guid]}</target_guid>
|
||||
<target_type>#{data[:target_type]}</target_type>
|
||||
<author>#{data[:author]}</author>
|
||||
<target_author_signature>#{data[:target_author_signature]}</target_author_signature>
|
||||
<sender_handle>#{data[:author]}</sender_handle>
|
||||
<target_author_signature/>
|
||||
</signed_retraction>
|
||||
XML
|
||||
|
||||
let(:string) { "SignedRetraction:#{data[:target_type]}:#{data[:target_guid]}" }
|
||||
|
||||
it_behaves_like "an Entity subclass"
|
||||
|
||||
it_behaves_like "an XML Entity", [:target_author_signature]
|
||||
|
||||
it_behaves_like "a retraction"
|
||||
|
||||
describe "#to_xml" do
|
||||
let(:author_pkey) { OpenSSL::PKey::RSA.generate(1024) }
|
||||
let(:hash) { Fabricate.attributes_for(:signed_retraction_entity) }
|
||||
|
||||
it "updates author signature when it was nil and key was supplied" do
|
||||
expect_callback(:fetch_private_key, hash[:author]).and_return(author_pkey)
|
||||
|
||||
signed_string = "#{hash[:target_guid]};#{hash[:target_type]}"
|
||||
|
||||
xml = Entities::SignedRetraction.new(hash).to_xml
|
||||
|
||||
signature = Base64.decode64(xml.at_xpath("target_author_signature").text)
|
||||
expect(author_pkey.verify(OpenSSL::Digest::SHA256.new, signature, signed_string)).to be_truthy
|
||||
end
|
||||
|
||||
it "doesn't change signature if it is already set" do
|
||||
hash[:target_author_signature] = "aa"
|
||||
|
||||
xml = Entities::SignedRetraction.new(hash).to_xml
|
||||
|
||||
expect(xml.at_xpath("target_author_signature").text).to eq("aa")
|
||||
end
|
||||
|
||||
it "doesn't change signature if a key wasn't supplied" do
|
||||
expect_callback(:fetch_private_key, hash[:author]).and_return(nil)
|
||||
|
||||
xml = Entities::SignedRetraction.new(hash).to_xml
|
||||
expect(xml.at_xpath("target_author_signature").text).to eq("")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_retraction" do
|
||||
it "copies the attributes to a Retraction" do
|
||||
signed_retraction = Fabricate(:signed_retraction_entity)
|
||||
retraction = signed_retraction.to_retraction
|
||||
|
||||
expect(retraction).to be_a(Entities::Retraction)
|
||||
expect(retraction.author).to eq(signed_retraction.author)
|
||||
expect(retraction.target_guid).to eq(signed_retraction.target_guid)
|
||||
expect(retraction.target_type).to eq(signed_retraction.target_type)
|
||||
describe "#initialize" do
|
||||
it "raises because it is not supported anymore" do
|
||||
expect {
|
||||
Entities::SignedRetraction.new(data)
|
||||
}.to raise_error RuntimeError,
|
||||
"Sending SignedRetraction is not supported anymore! Use Retraction instead!"
|
||||
end
|
||||
end
|
||||
|
||||
context "parse retraction" do
|
||||
it "parses the xml as a retraction" do
|
||||
expect(Entities::Retraction).to receive(:fetch_target).and_return(target_entity)
|
||||
retraction = Entities::SignedRetraction.from_xml(Nokogiri::XML::Document.parse(xml).root)
|
||||
expect(retraction).to be_a(Entities::Retraction)
|
||||
expect(retraction.author).to eq(data[:author])
|
||||
expect(retraction.target_guid).to eq(data[:target_guid])
|
||||
expect(retraction.target_type).to eq(data[:target_type])
|
||||
expect(retraction.target).to eq(target_entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
module DiasporaFederation
|
||||
describe Validators::RelayableRetractionValidator do
|
||||
let(:entity) { :relayable_retraction_entity }
|
||||
it_behaves_like "a common validator"
|
||||
|
||||
it_behaves_like "a diaspora* ID validator" do
|
||||
let(:property) { :author }
|
||||
let(:mandatory) { true }
|
||||
end
|
||||
|
||||
it_behaves_like "a guid validator" do
|
||||
let(:property) { :target_guid }
|
||||
end
|
||||
|
||||
describe "#target_type" do
|
||||
it_behaves_like "a property that mustn't be empty" do
|
||||
let(:property) { :target_type }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#target" do
|
||||
it_behaves_like "a property with a value validation/restriction" do
|
||||
let(:property) { :target }
|
||||
let(:wrong_values) { [nil] }
|
||||
let(:correct_values) { [Fabricate(:related_entity)] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
module DiasporaFederation
|
||||
describe Validators::SignedRetractionValidator do
|
||||
let(:entity) { :signed_retraction_entity }
|
||||
it_behaves_like "a common validator"
|
||||
|
||||
it_behaves_like "a diaspora* ID validator" do
|
||||
let(:property) { :author }
|
||||
let(:mandatory) { true }
|
||||
end
|
||||
|
||||
it_behaves_like "a guid validator" do
|
||||
let(:property) { :target_guid }
|
||||
end
|
||||
|
||||
describe "#target_type" do
|
||||
it_behaves_like "a property that mustn't be empty" do
|
||||
let(:property) { :target_type }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#target" do
|
||||
it_behaves_like "a property with a value validation/restriction" do
|
||||
let(:property) { :target }
|
||||
let(:wrong_values) { [nil] }
|
||||
let(:correct_values) { [Fabricate(:related_entity)] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -120,21 +120,6 @@ shared_examples "a relayable Entity" do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples "a retraction" do
|
||||
context "receive with no target found" do
|
||||
let(:unknown_guid) { Fabricate.sequence(:guid) }
|
||||
let(:instance) { described_class.new(data.merge(target_guid: unknown_guid)) }
|
||||
|
||||
it "raises when no target is found" do
|
||||
xml = instance.to_xml
|
||||
expect {
|
||||
described_class.from_xml(xml)
|
||||
}.to raise_error DiasporaFederation::Entities::Retraction::TargetNotFound,
|
||||
"not found: #{data[:target_type]}:#{unknown_guid}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples "a JSON Entity" do
|
||||
describe "#to_json" do
|
||||
it "#to_json output matches JSON schema" do
|
||||
|
|
|
|||
Loading…
Reference in a new issue