Merge pull request #114 from SuperTux88/remove-old-federation
Remove old federation
This commit is contained in:
commit
7fd94438a3
59 changed files with 146 additions and 1345 deletions
|
|
@ -5,18 +5,14 @@ require_dependency "diaspora_federation/application_controller"
|
|||
module DiasporaFederation
|
||||
# This controller processes receiving messages.
|
||||
class ReceiveController < ApplicationController
|
||||
before_action :check_for_xml
|
||||
|
||||
# Receives public messages
|
||||
#
|
||||
# POST /receive/public
|
||||
def public
|
||||
legacy = request.content_type != "application/magic-envelope+xml"
|
||||
|
||||
data = data_for_public_message(legacy)
|
||||
data = request.body.read
|
||||
logger.debug data
|
||||
|
||||
DiasporaFederation.callbacks.trigger(:queue_public_receive, data, legacy)
|
||||
DiasporaFederation.callbacks.trigger(:queue_public_receive, data)
|
||||
|
||||
head :accepted
|
||||
end
|
||||
|
|
@ -25,43 +21,12 @@ module DiasporaFederation
|
|||
#
|
||||
# POST /receive/users/:guid
|
||||
def private
|
||||
legacy = request.content_type != "application/json"
|
||||
|
||||
data = data_for_private_message(legacy)
|
||||
data = request.body.read
|
||||
logger.debug data
|
||||
|
||||
success = DiasporaFederation.callbacks.trigger(:queue_private_receive, params[:guid], data, legacy)
|
||||
success = DiasporaFederation.callbacks.trigger(:queue_private_receive, params[:guid], data)
|
||||
|
||||
head success ? :accepted : :not_found
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Checks the xml parameter for legacy salmon slaps
|
||||
# @deprecated
|
||||
def check_for_xml
|
||||
legacy_request = request.content_type.nil? || request.content_type == "application/x-www-form-urlencoded"
|
||||
head :unprocessable_entity if params[:xml].nil? && legacy_request
|
||||
end
|
||||
|
||||
def data_for_public_message(legacy)
|
||||
if legacy
|
||||
logger.info "received a public salmon slap"
|
||||
CGI.unescape(params[:xml])
|
||||
else
|
||||
logger.info "received a public magic envelope"
|
||||
request.body.read
|
||||
end
|
||||
end
|
||||
|
||||
def data_for_private_message(legacy)
|
||||
if legacy
|
||||
logger.info "received a private salmon slap for #{params[:guid]}"
|
||||
CGI.unescape(params[:xml])
|
||||
else
|
||||
logger.info "received a private encrypted magic envelope for #{params[:guid]}"
|
||||
request.body.read
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ See also: [Relayable][relayable]
|
|||
<parent_guid>bb8371f0b1c901342ebd55853a9b5d75</parent_guid>
|
||||
<status>accepted</status>
|
||||
<author_signature>dT6KbT7kp0bE+s3//ZErxO1wvVIqtD0lY67i81+dO43B4D2m5kjCdzW240eWt/jZmcHIsdxXf4WHNdrb6ZDnamA8I1FUVnLjHA9xexBITQsSLXrcV88UdammSmmOxl1Ac4VUXqFpdavm6a7/MwOJ7+JHP8TbUO9siN+hMfgUbtY=</author_signature>
|
||||
<parent_author_signature/>
|
||||
</event_participation>
|
||||
~~~
|
||||
|
||||
|
|
@ -46,7 +45,6 @@ See also: [Relayable][relayable]
|
|||
<parent_guid>bb8371f0b1c901342ebd55853a9b5d75</parent_guid>
|
||||
<status>accepted</status>
|
||||
<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>
|
||||
~~~
|
||||
|
||||
|
|
|
|||
|
|
@ -184,13 +184,11 @@ module DiasporaFederation
|
|||
# queue_public_receive
|
||||
# Queue a public salmon xml to process in background
|
||||
# @param [String] data salmon slap xml or magic envelope xml
|
||||
# @param [Boolean] legacy true if it is a legacy salmon slap, false if it is a magic envelope xml
|
||||
#
|
||||
# queue_private_receive
|
||||
# Queue a private salmon xml to process in background
|
||||
# @param [String] guid guid of the receiver person
|
||||
# @param [String] data salmon slap xml or encrypted magic envelope json
|
||||
# @param [Boolean] legacy true if it is a legacy salmon slap, false if it is a encrypted magic envelope json
|
||||
# @return [Boolean] true if successful, false if the user was not found
|
||||
#
|
||||
# receive_entity
|
||||
|
|
|
|||
|
|
@ -46,8 +46,3 @@ require "diaspora_federation/entities/message"
|
|||
require "diaspora_federation/entities/conversation"
|
||||
|
||||
require "diaspora_federation/entities/retraction"
|
||||
|
||||
# deprecated
|
||||
require "diaspora_federation/entities/request"
|
||||
require "diaspora_federation/entities/signed_retraction"
|
||||
require "diaspora_federation/entities/relayable_retraction"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ module DiasporaFederation
|
|||
# Alias for author
|
||||
# @see AccountDeletion#author
|
||||
# @return [String] diaspora* ID
|
||||
property :author, :string, alias: :diaspora_id, xml_name: :diaspora_handle
|
||||
property :author, :string, alias: :diaspora_id
|
||||
|
||||
# @return [String] string representation of this object
|
||||
def to_s
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ module DiasporaFederation
|
|||
# The diaspora* ID of the person initiated the conversation
|
||||
# @see Person#author
|
||||
# @return [String] diaspora* ID
|
||||
property :author, :string, xml_name: :diaspora_handle
|
||||
property :author, :string
|
||||
|
||||
# @!attribute [r] guid
|
||||
# A random string of at least 16 chars
|
||||
|
|
@ -29,7 +29,7 @@ module DiasporaFederation
|
|||
# @!attribute [r] participants
|
||||
# The diaspora* IDs of the persons participating the conversation separated by ";"
|
||||
# @return [String] participants diaspora* IDs
|
||||
property :participants, :string, xml_name: :participant_handles
|
||||
property :participants, :string
|
||||
|
||||
# @!attribute [r] messages
|
||||
# @return [[Entities::Message]] Messages of this conversation
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ module DiasporaFederation
|
|||
# Can be "Post" or "Comment" (Comments are currently not implemented in the
|
||||
# diaspora* frontend).
|
||||
# @return [String] parent type
|
||||
property :parent_type, :string, xml_name: :target_type
|
||||
property :parent_type, :string
|
||||
|
||||
# @!attribute [r] positive
|
||||
# If +true+ set a like, if +false+, set a dislike (dislikes are currently not
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ module DiasporaFederation
|
|||
# The diaspora* ID of the author
|
||||
# @see Person#author
|
||||
# @return [String] diaspora* ID
|
||||
property :author, :string, xml_name: :diaspora_handle
|
||||
property :author, :string
|
||||
|
||||
# @!attribute [r] guid
|
||||
# A random string of at least 16 chars
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ module DiasporaFederation
|
|||
# The diaspora* ID of the author
|
||||
# @see Person#author
|
||||
# @return [String] diaspora* ID
|
||||
property :author, :string, xml_name: :diaspora_handle
|
||||
property :author, :string
|
||||
|
||||
# @!attribute [r] guid
|
||||
# A random string of at least 16 chars
|
||||
|
|
@ -27,7 +27,7 @@ module DiasporaFederation
|
|||
# A string describing a type of the target to subscribe on
|
||||
# Currently only "Post" is supported.
|
||||
# @return [String] parent type
|
||||
property :parent_type, :string, xml_name: :target_type
|
||||
property :parent_type, :string
|
||||
|
||||
# @return [String] string representation of this object
|
||||
def to_s
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ module DiasporaFederation
|
|||
# alias for author
|
||||
# @see Person#author
|
||||
# @return [String] diaspora* ID
|
||||
property :author, :string, alias: :diaspora_id, xml_name: :diaspora_handle
|
||||
property :author, :string, alias: :diaspora_id
|
||||
|
||||
# @!attribute [r] url
|
||||
# @see Discovery::WebFinger#seed_url
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ module DiasporaFederation
|
|||
# The diaspora* ID of the person who uploaded the photo
|
||||
# @see Person#author
|
||||
# @return [String] author diaspora* ID
|
||||
property :author, :string, xml_name: :diaspora_handle
|
||||
property :author, :string
|
||||
|
||||
# @!attribute [r] public
|
||||
# Points if the photo is visible to everyone or only to some aspects
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ module DiasporaFederation
|
|||
# @param [Entity] entity the entity in which it is included
|
||||
def self.included(entity)
|
||||
entity.class_eval do
|
||||
property :author, :string, xml_name: :diaspora_handle
|
||||
property :author, :string
|
||||
property :guid, :string
|
||||
property :created_at, :timestamp, default: -> { Time.now.utc }
|
||||
property :public, :boolean, default: false
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ module DiasporaFederation
|
|||
# Alias for author
|
||||
# @see Profile#author
|
||||
# @return [String] diaspora* ID
|
||||
property :author, :string, alias: :diaspora_id, xml_name: :diaspora_handle
|
||||
property :author, :string, alias: :diaspora_id
|
||||
|
||||
# @!attribute [r] edited_at
|
||||
# The timestamp when the profile was edited
|
||||
|
|
|
|||
|
|
@ -35,12 +35,6 @@ module DiasporaFederation
|
|||
# a target pod.
|
||||
# @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
|
||||
# Meta information about the parent object
|
||||
# @return [RelatedEntity] parent entity
|
||||
|
|
@ -48,11 +42,10 @@ module DiasporaFederation
|
|||
# @param [Entity] klass the entity in which it is included
|
||||
def self.included(klass)
|
||||
klass.class_eval do
|
||||
property :author, :string, xml_name: :diaspora_handle
|
||||
property :author, :string
|
||||
property :guid, :string
|
||||
property :parent_guid, :string
|
||||
property :author_signature, :string, default: nil
|
||||
property :parent_author_signature, :string, default: nil
|
||||
entity :parent, Entities::RelatedEntity
|
||||
end
|
||||
|
||||
|
|
@ -67,7 +60,7 @@ module DiasporaFederation
|
|||
# @see DiasporaFederation::Entity#initialize
|
||||
def initialize(data, signature_order=nil, additional_data={})
|
||||
self.signature_order = signature_order if signature_order
|
||||
@additional_data = additional_data
|
||||
self.additional_data = additional_data
|
||||
|
||||
super(data)
|
||||
end
|
||||
|
|
@ -104,7 +97,7 @@ module DiasporaFederation
|
|||
def signature_order
|
||||
@signature_order || self.class.class_props.keys.reject {|key|
|
||||
self.class.optional_props.include?(key) && public_send(key).nil?
|
||||
} - %i[author_signature parent_author_signature parent]
|
||||
} - %i[author_signature parent]
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -121,17 +114,6 @@ module DiasporaFederation
|
|||
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
|
||||
# if the signatures are not there yet and if the keys are available.
|
||||
#
|
||||
|
|
@ -139,7 +121,6 @@ module DiasporaFederation
|
|||
def enriched_properties
|
||||
super.merge(additional_data).tap do |hash|
|
||||
hash[:author_signature] = author_signature || sign_with_author
|
||||
hash.delete(:parent_author_signature)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -147,10 +128,8 @@ module DiasporaFederation
|
|||
#
|
||||
# @return [Hash] sorted xml elements
|
||||
def xml_elements
|
||||
data = super.tap do |hash|
|
||||
hash[:parent_author_signature] = parent_author_signature || sign_with_parent_author_if_available.to_s
|
||||
end
|
||||
order = signature_order + %i[author_signature parent_author_signature]
|
||||
data = super
|
||||
order = signature_order + %i[author_signature]
|
||||
order.map {|element| [element, data[element].to_s] }.to_h
|
||||
end
|
||||
|
||||
|
|
@ -160,6 +139,10 @@ module DiasporaFederation
|
|||
.map {|name| prop_names.include?(name) ? name.to_sym : name }
|
||||
end
|
||||
|
||||
def additional_data=(additional_data)
|
||||
@additional_data = additional_data.reject {|name, _| name =~ /signature/ }
|
||||
end
|
||||
|
||||
# @return [String] signature data string
|
||||
def signature_data
|
||||
data = normalized_properties.merge(additional_data)
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiasporaFederation
|
||||
module Entities
|
||||
# This entity represents a claim of deletion of a previously federated
|
||||
# relayable entity. ({Entities::Comment}, {Entities::Like})
|
||||
#
|
||||
# There are two cases of federation of the RelayableRetraction.
|
||||
# Retraction from the dowstream object owner is when an author of the
|
||||
# relayable (e.g. Comment) deletes it themself. In this case only target_author_signature
|
||||
# is filled and a retraction is sent to the commented post's author. Here
|
||||
# the upstream object owner signs it with the parent's author key, puts
|
||||
# the signature in parent_author_signature and sends it to other pods where
|
||||
# other participating people are present. This is the second case - retraction
|
||||
# from the upstream object owner.
|
||||
# Retraction from the upstream object owner can also be performed by the
|
||||
# upstream object owner themself - they have a right to delete comments on their posts.
|
||||
# In any case in the retraction by the upstream author target_author_signature
|
||||
# is not checked, only parent_author_signature is checked.
|
||||
#
|
||||
# @see Validators::RelayableRetractionValidator
|
||||
# @deprecated will be replaced with {Entities::Retraction}
|
||||
class RelayableRetraction < Entity
|
||||
# @!attribute [r] parent_author_signature
|
||||
# Contains a signature of the entity using the private key of the author of a parent post.
|
||||
# This signature is mandatory only when federating from an upstream author to the subscribers.
|
||||
# @see Relayable#parent_author_signature
|
||||
# @return [String] parent author signature
|
||||
property :parent_author_signature, :string, default: nil
|
||||
|
||||
# @!attribute [r] target_guid
|
||||
# Guid of a relayable to be deleted
|
||||
# @see Comment#guid
|
||||
# @return [String] target guid
|
||||
property :target_guid, :string
|
||||
|
||||
# @!attribute [r] target_type
|
||||
# A string describing a type of the target
|
||||
# @see Retraction#target_type
|
||||
# @return [String] target type
|
||||
property :target_type, :string
|
||||
|
||||
# @!attribute [r] author
|
||||
# The diaspora* ID of the person who deletes a relayable
|
||||
# @see Person#author
|
||||
# @return [String] diaspora* ID
|
||||
property :author, :string, xml_name: :sender_handle
|
||||
|
||||
# @!attribute [r] target_author_signature
|
||||
# Contains a signature of the entity using the private key of the
|
||||
# author of a federated relayable entity. ({Entities::Comment}, {Entities::Like})
|
||||
# This signature is mandatory only when federation from the subscriber to an upstream
|
||||
# author is done.
|
||||
# @see Relayable#author_signature
|
||||
# @return [String] target author signature
|
||||
property :target_author_signature, :string, default: nil
|
||||
|
||||
def initialize(*)
|
||||
raise "Sending RelayableRetraction is not supported anymore! Use Retraction instead!"
|
||||
end
|
||||
|
||||
# @return [Retraction] instance
|
||||
def self.from_hash(hash)
|
||||
Retraction.from_hash(hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiasporaFederation
|
||||
module Entities
|
||||
# This entity represents a sharing request for a user. A user issues it
|
||||
# when they start sharing with another user.
|
||||
#
|
||||
# @see Validators::RequestValidator
|
||||
# @deprecated will be replaced with {Contact}
|
||||
class Request < Entity
|
||||
# @!attribute [r] author
|
||||
# The diaspora* ID of the person who share their profile
|
||||
# @see Person#author
|
||||
# @return [String] sender ID
|
||||
property :author, :string, xml_name: :sender_handle
|
||||
|
||||
# @!attribute [r] recipient
|
||||
# The diaspora* ID of the person who will be shared with
|
||||
# @see Validation::Rule::DiasporaId
|
||||
# @return [String] recipient ID
|
||||
property :recipient, :string, xml_name: :recipient_handle
|
||||
|
||||
def initialize(*)
|
||||
raise "Sending Request is not supported anymore! Use Contact instead!"
|
||||
end
|
||||
|
||||
# @return [Retraction] instance
|
||||
def self.from_hash(hash)
|
||||
Contact.new(hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -10,7 +10,7 @@ module DiasporaFederation
|
|||
# The diaspora* ID of the person who reshares the post
|
||||
# @see Person#author
|
||||
# @return [String] diaspora* ID
|
||||
property :author, :string, xml_name: :diaspora_handle
|
||||
property :author, :string
|
||||
|
||||
# @!attribute [r] guid
|
||||
# A random string of at least 16 chars
|
||||
|
|
@ -27,7 +27,7 @@ module DiasporaFederation
|
|||
# The diaspora* ID of the person who posted the original post
|
||||
# @see Person#author
|
||||
# @return [String] diaspora* ID
|
||||
property :root_author, :string, optional: true, xml_name: :root_diaspora_id
|
||||
property :root_author, :string, optional: true
|
||||
|
||||
# @!attribute [r] root_guid
|
||||
# Guid of the original post
|
||||
|
|
|
|||
|
|
@ -10,17 +10,17 @@ module DiasporaFederation
|
|||
# The diaspora* ID of the person who deletes the entity
|
||||
# @see Person#author
|
||||
# @return [String] diaspora* ID
|
||||
property :author, :string, xml_name: :diaspora_handle
|
||||
property :author, :string
|
||||
|
||||
# @!attribute [r] target_guid
|
||||
# Guid of the entity to be deleted
|
||||
# @return [String] target guid
|
||||
property :target_guid, :string, xml_name: :post_guid
|
||||
property :target_guid, :string
|
||||
|
||||
# @!attribute [r] target_type
|
||||
# A string describing the type of the target
|
||||
# @return [String] target type
|
||||
property :target_type, :string, xml_name: :type
|
||||
property :target_type, :string
|
||||
|
||||
# @!attribute [r] target
|
||||
# Target entity
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiasporaFederation
|
||||
module Entities
|
||||
# This entity represents a claim of deletion of a previously federated
|
||||
# entity of post type. ({Entities::StatusMessage})
|
||||
#
|
||||
# @see Validators::SignedRetractionValidator
|
||||
# @deprecated will be replaced with {Entities::Retraction}
|
||||
class SignedRetraction < Entity
|
||||
# @!attribute [r] target_guid
|
||||
# Guid of a post to be deleted
|
||||
# @see Retraction#target_guid
|
||||
# @return [String] target guid
|
||||
property :target_guid, :string
|
||||
|
||||
# @!attribute [r] target_type
|
||||
# A string describing the type of the target
|
||||
# @see Retraction#target_type
|
||||
# @return [String] target type
|
||||
property :target_type, :string
|
||||
|
||||
# @!attribute [r] author
|
||||
# The diaspora* ID of the person who deletes a post
|
||||
# @see Person#author
|
||||
# @return [String] diaspora* ID
|
||||
property :author, :string, xml_name: :sender_handle
|
||||
|
||||
# @!attribute [r] author_signature
|
||||
# Contains a signature of the entity using the private key of the author of a post
|
||||
# This signature is mandatory.
|
||||
# @return [String] author signature
|
||||
property :target_author_signature, :string, default: nil
|
||||
|
||||
def initialize(*)
|
||||
raise "Sending SignedRetraction is not supported anymore! Use Retraction instead!"
|
||||
end
|
||||
|
||||
# @return [Retraction] instance
|
||||
def self.from_hash(hash)
|
||||
Retraction.from_hash(hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -11,7 +11,7 @@ module DiasporaFederation
|
|||
# @!attribute [r] text
|
||||
# Text of the status message composed by the user
|
||||
# @return [String] text of the status message
|
||||
property :text, :string, xml_name: :raw_message
|
||||
property :text, :string
|
||||
|
||||
# @!attribute [r] edited_at
|
||||
# The timestamp when the status message was edited
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ module DiasporaFederation
|
|||
# property :prop
|
||||
# property :optional, default: false
|
||||
# property :dynamic_default, default: -> { Time.now }
|
||||
# property :another_prop, xml_name: :another_name
|
||||
# entity :nested, NestedEntity
|
||||
# entity :multiple, [OtherEntity]
|
||||
# end
|
||||
|
|
|
|||
|
|
@ -8,14 +8,9 @@ module DiasporaFederation
|
|||
|
||||
# Receive a public message
|
||||
# @param [String] data message to receive
|
||||
# @param [Boolean] legacy use old slap parser
|
||||
def self.receive_public(data, legacy=false)
|
||||
magic_env = if legacy
|
||||
Salmon::Slap.from_xml(data)
|
||||
else
|
||||
magic_env_xml = Nokogiri::XML(data).root
|
||||
Salmon::MagicEnvelope.unenvelop(magic_env_xml)
|
||||
end
|
||||
def self.receive_public(data)
|
||||
magic_env_xml = Nokogiri::XML(data).root
|
||||
magic_env = Salmon::MagicEnvelope.unenvelop(magic_env_xml)
|
||||
Public.new(magic_env).receive
|
||||
rescue => e # rubocop:disable Style/RescueStandardError
|
||||
logger.error "failed to receive public message: #{e.class}: #{e.message}"
|
||||
|
|
@ -28,16 +23,11 @@ module DiasporaFederation
|
|||
# @param [OpenSSL::PKey::RSA] recipient_private_key recipient private key to decrypt the message
|
||||
# @param [Object] recipient_id the identifier to persist the entity for the correct user,
|
||||
# see +receive_entity+ callback
|
||||
# @param [Boolean] legacy use old slap parser
|
||||
def self.receive_private(data, recipient_private_key, recipient_id, legacy=false)
|
||||
def self.receive_private(data, recipient_private_key, recipient_id)
|
||||
raise ArgumentError, "no recipient key provided" unless recipient_private_key.instance_of?(OpenSSL::PKey::RSA)
|
||||
|
||||
magic_env = if legacy
|
||||
Salmon::EncryptedSlap.from_xml(data, recipient_private_key)
|
||||
else
|
||||
magic_env_xml = Salmon::EncryptedMagicEnvelope.decrypt(data, recipient_private_key)
|
||||
Salmon::MagicEnvelope.unenvelop(magic_env_xml)
|
||||
end
|
||||
magic_env_xml = Salmon::EncryptedMagicEnvelope.decrypt(data, recipient_private_key)
|
||||
magic_env = Salmon::MagicEnvelope.unenvelop(magic_env_xml)
|
||||
Private.new(magic_env, recipient_id).receive
|
||||
rescue => e # rubocop:disable Style/RescueStandardError
|
||||
logger.error "failed to receive private message for #{recipient_id}: #{e.class}: #{e.message}"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ module DiasporaFederation
|
|||
|
||||
def parse_entity_data(entity_data)
|
||||
hash = entity_data.map {|key, value|
|
||||
property = entity_type.find_property_for_xml_name(key)
|
||||
property = entity_type.class_props.keys.find {|name| name.to_s == key }
|
||||
if property
|
||||
type = entity_type.class_props[property]
|
||||
[property, parse_element_from_value(type, entity_data[key])]
|
||||
|
|
|
|||
|
|
@ -15,14 +15,12 @@ module DiasporaFederation
|
|||
from_xml_sanity_validation(root_node)
|
||||
|
||||
hash = root_node.element_children.map {|child|
|
||||
xml_name = child.name
|
||||
property = entity_type.find_property_for_xml_name(xml_name)
|
||||
property, type = find_property_for(child.name)
|
||||
if property
|
||||
type = class_properties[property]
|
||||
value = parse_element_from_node(xml_name, type, root_node)
|
||||
value = parse_element_from_node(child.name, type, root_node)
|
||||
[property, value]
|
||||
else
|
||||
[xml_name, child.text]
|
||||
[child.name, child.text]
|
||||
end
|
||||
}.to_h
|
||||
|
||||
|
|
@ -31,6 +29,18 @@ module DiasporaFederation
|
|||
|
||||
private
|
||||
|
||||
def find_property_for(xml_name)
|
||||
class_properties.find {|name, type|
|
||||
if type.instance_of?(Symbol)
|
||||
name.to_s == xml_name
|
||||
elsif type.instance_of?(Array)
|
||||
type.first.entity_name == xml_name
|
||||
elsif type.ancestors.include?(Entity)
|
||||
type.entity_name == xml_name
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# @param [String] name property name to parse
|
||||
# @param [Class, Symbol] type target type to parse
|
||||
# @param [Nokogiri::XML::Element] root_node XML node to parse
|
||||
|
|
@ -53,7 +63,6 @@ module DiasporaFederation
|
|||
# @return [String] data
|
||||
def parse_string_from_node(name, type, root_node)
|
||||
node = root_node.xpath(name.to_s)
|
||||
node = root_node.xpath(xml_names[name].to_s) if node.empty?
|
||||
parse_string(type, node.first.text) if node.any?
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ module DiasporaFederation
|
|||
# property :prop
|
||||
# property :optional, default: false
|
||||
# property :dynamic_default, default: -> { Time.now }
|
||||
# property :another_prop, xml_name: :another_name
|
||||
# property :original_prop, alias: :alias_prop
|
||||
# entity :nested, NestedEntity
|
||||
# entity :multiple, [OtherEntity]
|
||||
|
|
@ -24,7 +23,6 @@ module DiasporaFederation
|
|||
# @param [Hash] opts further options
|
||||
# @option opts [Object, #call] :default a default value, making the
|
||||
# property optional
|
||||
# @option opts [Symbol] :xml_name another name used for xml generation
|
||||
def property(name, type, opts={})
|
||||
raise InvalidType unless property_type_valid?(type)
|
||||
|
||||
|
|
@ -79,49 +77,14 @@ module DiasporaFederation
|
|||
}.to_h
|
||||
end
|
||||
|
||||
# @return [Symbol] alias for the xml-generation/parsing
|
||||
# @deprecated
|
||||
def xml_names
|
||||
@xml_names ||= {}
|
||||
end
|
||||
|
||||
# Finds a property by +xml_name+ or +name+
|
||||
# @param [String] xml_name name of the property from the received xml
|
||||
# @return [Hash] the property data
|
||||
def find_property_for_xml_name(xml_name)
|
||||
class_props.keys.find {|name| [name.to_s, xml_names[name].to_s].include?(xml_name) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# @deprecated
|
||||
def determine_xml_name(name, type, opts={})
|
||||
if !type.instance_of?(Symbol) && opts.has_key?(:xml_name)
|
||||
raise ArgumentError, "xml_name is not supported for nested entities"
|
||||
end
|
||||
|
||||
if type.instance_of?(Symbol)
|
||||
if opts.has_key? :xml_name
|
||||
raise InvalidName, "invalid xml_name" unless name_valid?(opts[:xml_name])
|
||||
|
||||
opts[:xml_name]
|
||||
else
|
||||
name
|
||||
end
|
||||
elsif type.instance_of?(Array)
|
||||
type.first.entity_name.to_sym
|
||||
elsif type.ancestors.include?(Entity)
|
||||
type.entity_name.to_sym
|
||||
end
|
||||
end
|
||||
|
||||
def define_property(name, type, opts={})
|
||||
raise InvalidName unless name_valid?(name)
|
||||
|
||||
class_props[name] = type
|
||||
optional_props << name if opts[:optional]
|
||||
default_props[name] = opts[:default] if opts.has_key? :default
|
||||
xml_names[name] = determine_xml_name(name, type, opts)
|
||||
|
||||
instance_eval { attr_reader name }
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,5 @@ require "base64"
|
|||
|
||||
require "diaspora_federation/salmon/aes"
|
||||
require "diaspora_federation/salmon/exceptions"
|
||||
require "diaspora_federation/salmon/xml_payload"
|
||||
require "diaspora_federation/salmon/magic_envelope"
|
||||
require "diaspora_federation/salmon/encrypted_magic_envelope"
|
||||
require "diaspora_federation/salmon/slap"
|
||||
require "diaspora_federation/salmon/encrypted_slap"
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "json"
|
||||
|
||||
module DiasporaFederation
|
||||
module Salmon
|
||||
# +EncryptedSlap+ provides class methods for generating and parsing encrypted
|
||||
# Slaps. (In principle the same as {Slap}, but with encryption.)
|
||||
#
|
||||
# The basic encryption mechanism used here is based on the knowledge that
|
||||
# asymmetrical encryption is slow and symmetrical encryption is fast. Keeping in
|
||||
# mind that a message we want to de-/encrypt may greatly vary in length,
|
||||
# performance considerations must play a part of this scheme.
|
||||
#
|
||||
# A diaspora*-flavored encrypted magic-enveloped XML message looks like the following:
|
||||
#
|
||||
# <?xml version="1.0" encoding="UTF-8"?>
|
||||
# <diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env">
|
||||
# <encrypted_header>{encrypted_header}</encrypted_header>
|
||||
# {magic_envelope with encrypted data}
|
||||
# </diaspora>
|
||||
#
|
||||
# The encrypted header is encoded in JSON like this (when in plain text):
|
||||
#
|
||||
# {
|
||||
# "aes_key" => "...",
|
||||
# "ciphertext" => "..."
|
||||
# }
|
||||
#
|
||||
# +aes_key+ is encrypted using the recipients public key, and contains the AES
|
||||
# +key+ and +iv+ used to encrypt the +ciphertext+ also encoded as JSON.
|
||||
#
|
||||
# {
|
||||
# "key" => "...",
|
||||
# "iv" => "..."
|
||||
# }
|
||||
#
|
||||
# +ciphertext+, once decrypted, contains the +author_id+, +aes_key+ and +iv+
|
||||
# relevant to the decryption of the data in the magic_envelope and the
|
||||
# verification of its signature.
|
||||
#
|
||||
# The decrypted cyphertext has this XML structure:
|
||||
#
|
||||
# <decrypted_header>
|
||||
# <iv>{iv}</iv>
|
||||
# <aes_key>{aes_key}</aes_key>
|
||||
# <author_id>{author_id}</author_id>
|
||||
# </decrypted_header>
|
||||
#
|
||||
# Finally, before decrypting the magic envelope payload, the signature should
|
||||
# first be verified.
|
||||
#
|
||||
# @example Parsing a Salmon Slap
|
||||
# recipient_privkey = however_you_retrieve_the_recipients_private_key()
|
||||
# entity = EncryptedSlap.from_xml(slap_xml, recipient_privkey).payload
|
||||
#
|
||||
# @deprecated
|
||||
class EncryptedSlap < Slap
|
||||
# Creates a {MagicEnvelope} instance from the data within the given XML string
|
||||
# containing an encrypted payload.
|
||||
#
|
||||
# @param [String] slap_xml encrypted Salmon xml
|
||||
# @param [OpenSSL::PKey::RSA] privkey recipient private_key for decryption
|
||||
#
|
||||
# @return [MagicEnvelope] magic envelope instance with payload and sender
|
||||
#
|
||||
# @raise [ArgumentError] if any of the arguments is of the wrong type
|
||||
# @raise [MissingHeader] if the +encrypted_header+ element is missing in the XML
|
||||
# @raise [MissingMagicEnvelope] if the +me:env+ element is missing in the XML
|
||||
def self.from_xml(slap_xml, privkey)
|
||||
raise ArgumentError unless slap_xml.instance_of?(String) && privkey.instance_of?(OpenSSL::PKey::RSA)
|
||||
|
||||
doc = Nokogiri::XML(slap_xml)
|
||||
|
||||
header_elem = doc.at_xpath("d:diaspora/d:encrypted_header", Slap::NS)
|
||||
raise MissingHeader if header_elem.nil?
|
||||
|
||||
header = header_data(header_elem.content, privkey)
|
||||
sender = header[:author_id]
|
||||
cipher_params = {key: Base64.decode64(header[:aes_key]), iv: Base64.decode64(header[:iv])}
|
||||
|
||||
MagicEnvelope.unenvelop(magic_env_from_doc(doc), sender, cipher_params)
|
||||
end
|
||||
|
||||
# Decrypts and reads the data from the encrypted XML header
|
||||
# @param [String] data base64 encoded, encrypted header data
|
||||
# @param [OpenSSL::PKey::RSA] privkey private key for decryption
|
||||
# @return [Hash] { iv: "...", aes_key: "...", author_id: "..." }
|
||||
private_class_method def self.header_data(data, privkey)
|
||||
header_elem = decrypt_header(data, privkey)
|
||||
raise InvalidHeader unless header_elem.name == "decrypted_header"
|
||||
|
||||
iv = header_elem.at_xpath("iv").content
|
||||
key = header_elem.at_xpath("aes_key").content
|
||||
author_id = header_elem.at_xpath("author_id").content
|
||||
|
||||
{iv: iv, aes_key: key, author_id: author_id}
|
||||
end
|
||||
|
||||
# Decrypts the xml header
|
||||
# @param [String] data base64 encoded, encrypted header data
|
||||
# @param [OpenSSL::PKey::RSA] privkey private key for decryption
|
||||
# @return [Nokogiri::XML::Element] header xml document
|
||||
private_class_method def self.decrypt_header(data, privkey)
|
||||
cipher_header = JSON.parse(Base64.decode64(data))
|
||||
key = JSON.parse(privkey.private_decrypt(Base64.decode64(cipher_header["aes_key"])))
|
||||
|
||||
xml = AES.decrypt(cipher_header["ciphertext"], Base64.decode64(key["key"]), Base64.decode64(key["iv"]))
|
||||
Nokogiri::XML(xml).root
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2,26 +2,6 @@
|
|||
|
||||
module DiasporaFederation
|
||||
module Salmon
|
||||
# Raised, if the element containing the Magic Envelope is missing from the XML
|
||||
# @deprecated
|
||||
class MissingMagicEnvelope < RuntimeError
|
||||
end
|
||||
|
||||
# Raised, if the element containing the author is empty.
|
||||
# @deprecated
|
||||
class MissingAuthor < RuntimeError
|
||||
end
|
||||
|
||||
# Raised, if the element containing the header is missing from the XML
|
||||
# @deprecated
|
||||
class MissingHeader < RuntimeError
|
||||
end
|
||||
|
||||
# Raised, if the decrypted header has an unexpected XML structure
|
||||
# @deprecated
|
||||
class InvalidHeader < RuntimeError
|
||||
end
|
||||
|
||||
# Raised, if failed to fetch the public key of the sender of the received message
|
||||
class SenderKeyNotFound < RuntimeError
|
||||
end
|
||||
|
|
|
|||
|
|
@ -114,7 +114,8 @@ module DiasporaFederation
|
|||
|
||||
logger.debug "unenvelop message from #{sender}:\n#{data}"
|
||||
|
||||
new(XmlPayload.unpack(Nokogiri::XML(data).root), sender)
|
||||
xml = Nokogiri::XML(data).root
|
||||
new(Entity.entity_class(xml.name).from_xml(xml), sender)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiasporaFederation
|
||||
module Salmon
|
||||
# +Slap+ provides class methods to create unencrypted Slap XML from payload
|
||||
# data and parse incoming XML into a Slap instance.
|
||||
#
|
||||
# A diaspora* flavored magic-enveloped XML message looks like the following:
|
||||
#
|
||||
# <?xml version="1.0" encoding="UTF-8"?>
|
||||
# <diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env">
|
||||
# <header>
|
||||
# <author_id>{author}</author_id>
|
||||
# </header>
|
||||
# {magic_envelope}
|
||||
# </diaspora>
|
||||
#
|
||||
# @example Parsing a Salmon Slap
|
||||
# entity = Slap.from_xml(slap_xml).payload
|
||||
#
|
||||
# @deprecated
|
||||
class Slap
|
||||
# Namespaces
|
||||
NS = {d: Salmon::XMLNS, me: MagicEnvelope::XMLNS}.freeze
|
||||
|
||||
# Parses an unencrypted Salmon XML string and returns a new instance of
|
||||
# {MagicEnvelope} with the XML data.
|
||||
#
|
||||
# @param [String] slap_xml Salmon XML
|
||||
#
|
||||
# @return [MagicEnvelope] magic envelope instance with payload and sender
|
||||
#
|
||||
# @raise [ArgumentError] if the argument is not a String
|
||||
# @raise [MissingAuthor] if the +author_id+ element is missing from the XML
|
||||
# @raise [MissingMagicEnvelope] if the +me:env+ element is missing from the XML
|
||||
def self.from_xml(slap_xml)
|
||||
raise ArgumentError unless slap_xml.instance_of?(String)
|
||||
|
||||
doc = Nokogiri::XML(slap_xml)
|
||||
|
||||
author_elem = doc.at_xpath("d:diaspora/d:header/d:author_id", Slap::NS)
|
||||
raise MissingAuthor if author_elem.nil? || author_elem.content.empty?
|
||||
|
||||
sender = author_elem.content
|
||||
|
||||
MagicEnvelope.unenvelop(magic_env_from_doc(doc), sender)
|
||||
end
|
||||
|
||||
# Parses the magic envelop from the document.
|
||||
#
|
||||
# @param [Nokogiri::XML::Document] doc Salmon XML Document
|
||||
private_class_method def self.magic_env_from_doc(doc)
|
||||
doc.at_xpath("d:diaspora/me:env", Slap::NS).tap do |env|
|
||||
raise MissingMagicEnvelope if env.nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiasporaFederation
|
||||
module Salmon
|
||||
# +XmlPayload+ provides methods to wrap a XML-serialized {Entity} inside a
|
||||
# common XML structure that will become the payload for federation messages.
|
||||
#
|
||||
# The wrapper looks like so:
|
||||
# <XML>
|
||||
# <post>
|
||||
# {data}
|
||||
# </post>
|
||||
# </XML>
|
||||
#
|
||||
# (The +post+ element is there for historic reasons...)
|
||||
# @deprecated
|
||||
module XmlPayload
|
||||
# Extracts the Entity XML from the wrapping XML structure, parses the entity
|
||||
# XML and returns a new instance of the Entity that was packed inside the
|
||||
# given payload.
|
||||
#
|
||||
# @param [Nokogiri::XML::Element] xml payload XML root node
|
||||
# @return [Entity] re-constructed Entity instance
|
||||
# @raise [ArgumentError] if the argument is not an
|
||||
# {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Element Nokogiri::XML::Element}
|
||||
# @raise [UnknownEntity] if the class for the entity contained inside the
|
||||
# XML can't be found
|
||||
def self.unpack(xml)
|
||||
raise ArgumentError, "only Nokogiri::XML::Element allowed" unless xml.instance_of?(Nokogiri::XML::Element)
|
||||
|
||||
data = xml_wrapped?(xml) ? xml.at_xpath("post/*[1]") : xml
|
||||
|
||||
Entity.entity_class(data.name).from_xml(data)
|
||||
end
|
||||
|
||||
# @param [Nokogiri::XML::Element] element
|
||||
private_class_method def self.xml_wrapped?(element)
|
||||
(element.name == "XML" && !element.at_xpath("post").nil? &&
|
||||
!element.at_xpath("post").children.empty?)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,32 +5,6 @@ module DiasporaFederation
|
|||
routes { DiasporaFederation::Engine.routes }
|
||||
|
||||
describe "POST #public" do
|
||||
context "legacy salmon slap" do
|
||||
it "returns a 422 if no xml is passed" do
|
||||
post :public
|
||||
expect(response.code).to eq("422")
|
||||
end
|
||||
|
||||
it "returns a 422 if no xml is passed with content-type application/x-www-form-urlencoded" do
|
||||
@request.env["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
|
||||
post :public
|
||||
expect(response.code).to eq("422")
|
||||
end
|
||||
|
||||
it "returns a 202 if queued correctly" do
|
||||
expect_callback(:queue_public_receive, "<diaspora/>", true)
|
||||
|
||||
post :public, params: {xml: "<diaspora/>"}
|
||||
expect(response.code).to eq("202")
|
||||
end
|
||||
|
||||
it "unescapes the xml before sending it to the callback" do
|
||||
expect_callback(:queue_public_receive, "<diaspora/>", true)
|
||||
|
||||
post :public, params: {xml: CGI.escape("<diaspora/>")}
|
||||
end
|
||||
end
|
||||
|
||||
context "magic envelope" do
|
||||
before do
|
||||
Mime::Type.register("application/magic-envelope+xml", :magic_envelope)
|
||||
|
|
@ -38,7 +12,7 @@ module DiasporaFederation
|
|||
end
|
||||
|
||||
it "returns a 202 if queued correctly" do
|
||||
expect_callback(:queue_public_receive, "<me:env/>", false)
|
||||
expect_callback(:queue_public_receive, "<me:env/>")
|
||||
|
||||
post :public, body: +"<me:env/>"
|
||||
expect(response.code).to eq("202")
|
||||
|
|
@ -47,39 +21,6 @@ module DiasporaFederation
|
|||
end
|
||||
|
||||
describe "POST #private" do
|
||||
context "legacy salmon slap" do
|
||||
it "return a 404 if not queued successfully (unknown user guid)" do
|
||||
expect_callback(:queue_private_receive, "any-guid", "<diaspora/>", true).and_return(false)
|
||||
|
||||
post :private, params: {guid: "any-guid", xml: "<diaspora/>"}
|
||||
expect(response.code).to eq("404")
|
||||
end
|
||||
|
||||
it "returns a 422 if no xml is passed" do
|
||||
post :private, params: {guid: "any-guid"}
|
||||
expect(response.code).to eq("422")
|
||||
end
|
||||
|
||||
it "returns a 422 if no xml is passed with content-type application/x-www-form-urlencoded" do
|
||||
@request.env["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
|
||||
post :private, params: {guid: "any-guid"}
|
||||
expect(response.code).to eq("422")
|
||||
end
|
||||
|
||||
it "returns a 202 if the callback returned true" do
|
||||
expect_callback(:queue_private_receive, "any-guid", "<diaspora/>", true).and_return(true)
|
||||
|
||||
post :private, params: {guid: "any-guid", xml: "<diaspora/>"}
|
||||
expect(response.code).to eq("202")
|
||||
end
|
||||
|
||||
it "unescapes the xml before sending it to the callback" do
|
||||
expect_callback(:queue_private_receive, "any-guid", "<diaspora/>", true).and_return(true)
|
||||
|
||||
post :private, params: {guid: "any-guid", xml: CGI.escape("<diaspora/>")}
|
||||
end
|
||||
end
|
||||
|
||||
context "encrypted magic envelope" do
|
||||
before do
|
||||
@request.env["CONTENT_TYPE"] = "application/json"
|
||||
|
|
@ -87,7 +28,7 @@ module DiasporaFederation
|
|||
|
||||
it "return a 404 if not queued successfully (unknown user guid)" do
|
||||
expect_callback(
|
||||
:queue_private_receive, "any-guid", "{\"aes_key\": \"key\", \"encrypted_magic_envelope\": \"env\"}", false
|
||||
:queue_private_receive, "any-guid", "{\"aes_key\": \"key\", \"encrypted_magic_envelope\": \"env\"}"
|
||||
).and_return(false)
|
||||
|
||||
post :private,
|
||||
|
|
@ -98,7 +39,7 @@ module DiasporaFederation
|
|||
|
||||
it "returns a 202 if the callback returned true" do
|
||||
expect_callback(
|
||||
:queue_private_receive, "any-guid", "{\"aes_key\": \"key\", \"encrypted_magic_envelope\": \"env\"}", false
|
||||
:queue_private_receive, "any-guid", "{\"aes_key\": \"key\", \"encrypted_magic_envelope\": \"env\"}"
|
||||
).and_return(true)
|
||||
|
||||
post :private,
|
||||
|
|
|
|||
|
|
@ -28,11 +28,6 @@ module DiasporaFederation
|
|||
entity :multi, [OtherEntity]
|
||||
end
|
||||
|
||||
class TestEntityWithXmlName < DiasporaFederation::Entity
|
||||
property :test, :string
|
||||
property :qwer, :string, xml_name: :asdf
|
||||
end
|
||||
|
||||
class TestEntityWithRelatedEntity < DiasporaFederation::Entity
|
||||
property :test, :string
|
||||
entity :parent, RelatedEntity
|
||||
|
|
|
|||
|
|
@ -27,31 +27,6 @@ module DiasporaFederation
|
|||
# g6zpg1zxGahpmxwqFQIDAQAB
|
||||
# -----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(:guid) { "e21589b0b41101333b870f77ba60fa73" }
|
||||
let(:parent_guid) { "9e269ae0b41201333b8c0f77ba60fa73" }
|
||||
|
|
@ -65,31 +40,16 @@ module DiasporaFederation
|
|||
)
|
||||
}
|
||||
|
||||
let(:legacy_format_comment_xml_alice) { <<~XML }
|
||||
<XML>
|
||||
<post>
|
||||
<comment>
|
||||
<guid>e21589b0b41101333b870f77ba60fa73</guid>
|
||||
<parent_guid>9e269ae0b41201333b8c0f77ba60fa73</parent_guid>
|
||||
<author_signature>XU5X1uqTh8SY6JMG9uhEVR5Rg7FURV6lpRwl/HYOu6DJ3Hd9tpA2aSFFibUxxsMgJXKNrrc5SykrrEdTiQoEei+j0QqZf3B5R7r84qgK7M46KazwIpqRPwVl2MdA/0DdQyYJLA/oavNj1nwll9vtR87M7e/C94qG6P+iQTMBQzo=</author_signature>
|
||||
<parent_author_signature/>
|
||||
<text>this is a very informative comment</text>
|
||||
<diaspora_handle>alice@pod-a.org</diaspora_handle>
|
||||
</comment>
|
||||
</post>
|
||||
</XML>
|
||||
XML
|
||||
let(:new_format_comment_xml_alice) { <<~XML }
|
||||
let(:comment_xml_alice) { <<~XML }
|
||||
<comment>
|
||||
<author>alice@pod-a.org</author>
|
||||
<guid>e21589b0b41101333b870f77ba60fa73</guid>
|
||||
<parent_guid>9e269ae0b41201333b8c0f77ba60fa73</parent_guid>
|
||||
<text>this is a very informative comment</text>
|
||||
<author_signature>SQbLeqsEpFmSl74G1fFJXKQcsq6jp5B2ZjmfEOF/LbBccYP2oZEyEqOq18K3Fa71OYTp6Nddb38hCmHWWHvnGUltGfxKBnQ0WHafJUi40VM4VmeRoU8cac6m+1hslwe5SNmK6oh47EV3mRCXlgGGjLIrw7iEwjKL2g9x1gkNp2s=</author_signature>
|
||||
<parent_author_signature/>
|
||||
</comment>
|
||||
XML
|
||||
let(:new_data_comment_xml_alice) { <<~XML }
|
||||
let(:comment_xml_alice_with_new_data) { <<~XML }
|
||||
<comment>
|
||||
<author>alice@pod-a.org</author>
|
||||
<guid>e21589b0b41101333b870f77ba60fa73</guid>
|
||||
|
|
@ -97,74 +57,28 @@ module DiasporaFederation
|
|||
<text>this is a very informative comment</text>
|
||||
<new_data>foobar</new_data>
|
||||
<author_signature>SFYXSvCX/DhTFiOUALp2Nf1kfNkGKXrnoBPikAyhaIogGydVBm+8tIlu1U/vsnpyKO3yfC3JReJ00/UBd4J16VO1IxStntq8NUqbSv4me5A/6kdK9Xg6eYbXrqQGm8fUQ5Xuh2UzeB71p7SVySXX3OZHVe0dvHCxH/lsfSDpEjc=</author_signature>
|
||||
<parent_author_signature/>
|
||||
</comment>
|
||||
XML
|
||||
|
||||
let(:legacy_format_comment_xml_bob) { <<~XML }
|
||||
<XML>
|
||||
<post>
|
||||
<comment>
|
||||
<guid>e21589b0b41101333b870f77ba60fa73</guid>
|
||||
<parent_guid>9e269ae0b41201333b8c0f77ba60fa73</parent_guid>
|
||||
<text>this is a very informative comment</text>
|
||||
<diaspora_handle>alice@pod-a.org</diaspora_handle>
|
||||
<author_signature>XU5X1uqTh8SY6JMG9uhEVR5Rg7FURV6lpRwl/HYOu6DJ3Hd9tpA2aSFFibUxxsMgJXKNrrc5SykrrEdTiQoEei+j0QqZf3B5R7r84qgK7M46KazwIpqRPwVl2MdA/0DdQyYJLA/oavNj1nwll9vtR87M7e/C94qG6P+iQTMBQzo=</author_signature>
|
||||
<parent_author_signature>QqWSdwpb+/dcJUxuKKVe7aiz1NivXzlIdWZ71xyrxnhFxFYd+7EIittyTcp1cVehjg96pwDbn++P/rWyCffqenWu025DHvUfSmQkC93Z0dX6r3OIUlZqwEggtOdbunybiE++F3BVsGt5wC4YbAESB5ZFuhFVhBXh1X+EaZ/qoKo=</parent_author_signature>
|
||||
</comment>
|
||||
</post>
|
||||
</XML>
|
||||
XML
|
||||
let(:legacy_order_new_format_comment_xml_bob) { <<~XML }
|
||||
let(:comment_xml_bob_legacy_order) { <<~XML }
|
||||
<comment>
|
||||
<guid>e21589b0b41101333b870f77ba60fa73</guid>
|
||||
<parent_guid>9e269ae0b41201333b8c0f77ba60fa73</parent_guid>
|
||||
<text>this is a very informative comment</text>
|
||||
<author>alice@pod-a.org</author>
|
||||
<author_signature>XU5X1uqTh8SY6JMG9uhEVR5Rg7FURV6lpRwl/HYOu6DJ3Hd9tpA2aSFFibUxxsMgJXKNrrc5SykrrEdTiQoEei+j0QqZf3B5R7r84qgK7M46KazwIpqRPwVl2MdA/0DdQyYJLA/oavNj1nwll9vtR87M7e/C94qG6P+iQTMBQzo=</author_signature>
|
||||
<parent_author_signature>QqWSdwpb+/dcJUxuKKVe7aiz1NivXzlIdWZ71xyrxnhFxFYd+7EIittyTcp1cVehjg96pwDbn++P/rWyCffqenWu025DHvUfSmQkC93Z0dX6r3OIUlZqwEggtOdbunybiE++F3BVsGt5wC4YbAESB5ZFuhFVhBXh1X+EaZ/qoKo=</parent_author_signature>
|
||||
</comment>
|
||||
XML
|
||||
let(:new_order_legacy_format_comment_xml_bob) { <<~XML }
|
||||
<XML>
|
||||
<post>
|
||||
<comment>
|
||||
<diaspora_handle>alice@pod-a.org</diaspora_handle>
|
||||
<guid>e21589b0b41101333b870f77ba60fa73</guid>
|
||||
<parent_guid>9e269ae0b41201333b8c0f77ba60fa73</parent_guid>
|
||||
<text>this is a very informative comment</text>
|
||||
<author_signature>SQbLeqsEpFmSl74G1fFJXKQcsq6jp5B2ZjmfEOF/LbBccYP2oZEyEqOq18K3Fa71OYTp6Nddb38hCmHWWHvnGUltGfxKBnQ0WHafJUi40VM4VmeRoU8cac6m+1hslwe5SNmK6oh47EV3mRCXlgGGjLIrw7iEwjKL2g9x1gkNp2s=</author_signature>
|
||||
<parent_author_signature>hWsagsczmZD6d36d6MFdTt3hKAdnRtupSIU6464G2kkMJ+WlExxMgbF6kWR+jVCBTeKipWCYK3Arnj0YkuIZM9d14bJGVMTsW/ZzNfJ69bXZhsyawI8dPnZnLVydo+hU/XmGJBEuh2TOj9Emq6/HCYiWzPTF5qhYAtyJ1oxJ4Yk=</parent_author_signature>
|
||||
</comment>
|
||||
</post>
|
||||
</XML>
|
||||
XML
|
||||
let(:new_format_comment_xml_bob) { <<~XML }
|
||||
let(:comment_xml_bob) { <<~XML }
|
||||
<comment>
|
||||
<author>alice@pod-a.org</author>
|
||||
<guid>e21589b0b41101333b870f77ba60fa73</guid>
|
||||
<parent_guid>9e269ae0b41201333b8c0f77ba60fa73</parent_guid>
|
||||
<text>this is a very informative comment</text>
|
||||
<author_signature>SQbLeqsEpFmSl74G1fFJXKQcsq6jp5B2ZjmfEOF/LbBccYP2oZEyEqOq18K3Fa71OYTp6Nddb38hCmHWWHvnGUltGfxKBnQ0WHafJUi40VM4VmeRoU8cac6m+1hslwe5SNmK6oh47EV3mRCXlgGGjLIrw7iEwjKL2g9x1gkNp2s=</author_signature>
|
||||
<parent_author_signature>hWsagsczmZD6d36d6MFdTt3hKAdnRtupSIU6464G2kkMJ+WlExxMgbF6kWR+jVCBTeKipWCYK3Arnj0YkuIZM9d14bJGVMTsW/ZzNfJ69bXZhsyawI8dPnZnLVydo+hU/XmGJBEuh2TOj9Emq6/HCYiWzPTF5qhYAtyJ1oxJ4Yk=</parent_author_signature>
|
||||
</comment>
|
||||
XML
|
||||
let(:legacy_format_new_data_comment_xml_bob) { <<~XML }
|
||||
<XML>
|
||||
<post>
|
||||
<comment>
|
||||
<diaspora_handle>alice@pod-a.org</diaspora_handle>
|
||||
<guid>e21589b0b41101333b870f77ba60fa73</guid>
|
||||
<parent_guid>9e269ae0b41201333b8c0f77ba60fa73</parent_guid>
|
||||
<text>this is a very informative comment</text>
|
||||
<new_data>foobar</new_data>
|
||||
<author_signature>SFYXSvCX/DhTFiOUALp2Nf1kfNkGKXrnoBPikAyhaIogGydVBm+8tIlu1U/vsnpyKO3yfC3JReJ00/UBd4J16VO1IxStntq8NUqbSv4me5A/6kdK9Xg6eYbXrqQGm8fUQ5Xuh2UzeB71p7SVySXX3OZHVe0dvHCxH/lsfSDpEjc=</author_signature>
|
||||
<parent_author_signature>NxXuEUVeXwUMR77osIbaNlp2oB3bpl8rBEFgQoO6cnoN5ewDbiGADK0x6EhcmJptjwhGVcZiNJNpq7k3/pjJtKaH++3ToCAtcuZoIKwPDsneLnjPhVjE2GXM1TiZKwoHrq41qSp/8Vl5UPbtC6sPiOzIvPKaILXUG8XCiVWuB0M=</parent_author_signature>
|
||||
</comment>
|
||||
</post>
|
||||
</XML>
|
||||
XML
|
||||
let(:new_data_comment_xml_bob) { <<~XML }
|
||||
let(:comment_xml_bob_with_new_data) { <<~XML }
|
||||
<comment>
|
||||
<author>alice@pod-a.org</author>
|
||||
<guid>e21589b0b41101333b870f77ba60fa73</guid>
|
||||
|
|
@ -172,7 +86,6 @@ module DiasporaFederation
|
|||
<text>this is a very informative comment</text>
|
||||
<new_data>foobar</new_data>
|
||||
<author_signature>SFYXSvCX/DhTFiOUALp2Nf1kfNkGKXrnoBPikAyhaIogGydVBm+8tIlu1U/vsnpyKO3yfC3JReJ00/UBd4J16VO1IxStntq8NUqbSv4me5A/6kdK9Xg6eYbXrqQGm8fUQ5Xuh2UzeB71p7SVySXX3OZHVe0dvHCxH/lsfSDpEjc=</author_signature>
|
||||
<parent_author_signature>NxXuEUVeXwUMR77osIbaNlp2oB3bpl8rBEFgQoO6cnoN5ewDbiGADK0x6EhcmJptjwhGVcZiNJNpq7k3/pjJtKaH++3ToCAtcuZoIKwPDsneLnjPhVjE2GXM1TiZKwoHrq41qSp/8Vl5UPbtC6sPiOzIvPKaILXUG8XCiVWuB0M=</parent_author_signature>
|
||||
</comment>
|
||||
XML
|
||||
|
||||
|
|
@ -180,44 +93,35 @@ module DiasporaFederation
|
|||
context "test-data creation" do
|
||||
it "creates comment xml" do
|
||||
expect_callback(:fetch_private_key, author).and_return(author_key)
|
||||
expect_callback(:fetch_private_key, parent.author).and_return(nil)
|
||||
|
||||
comment.to_xml
|
||||
end
|
||||
|
||||
it "creates relayed comment xml" do
|
||||
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)
|
||||
|
||||
xml = Nokogiri::XML(new_data_comment_xml_alice).root
|
||||
Salmon::XmlPayload.unpack(xml).to_xml
|
||||
xml = Nokogiri::XML(comment_xml_alice_with_new_data).root
|
||||
Entity.entity_class(xml.name).from_xml(xml).to_xml
|
||||
end
|
||||
end
|
||||
|
||||
context "relaying on bobs pod" do
|
||||
before do
|
||||
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)
|
||||
end
|
||||
|
||||
it "relays legacy order" do
|
||||
xml = Nokogiri::XML(legacy_format_comment_xml_alice).root
|
||||
entity = Salmon::XmlPayload.unpack(xml)
|
||||
expect(entity.to_xml.to_xml).to eq(legacy_order_new_format_comment_xml_bob.strip)
|
||||
end
|
||||
|
||||
it "relays new order" do
|
||||
xml = Nokogiri::XML(new_format_comment_xml_alice).root
|
||||
entity = Salmon::XmlPayload.unpack(xml)
|
||||
expect(entity.to_xml.to_xml).to eq(new_format_comment_xml_bob.strip)
|
||||
xml = Nokogiri::XML(comment_xml_alice).root
|
||||
entity = Entity.entity_class(xml.name).from_xml(xml)
|
||||
expect(entity.to_xml.to_xml).to eq(comment_xml_bob.strip)
|
||||
end
|
||||
|
||||
it "relays new data" do
|
||||
xml = Nokogiri::XML(new_data_comment_xml_alice).root
|
||||
entity = Salmon::XmlPayload.unpack(xml)
|
||||
expect(entity.to_xml.to_xml).to eq(new_data_comment_xml_bob.strip)
|
||||
xml = Nokogiri::XML(comment_xml_alice_with_new_data).root
|
||||
entity = Entity.entity_class(xml.name).from_xml(xml)
|
||||
expect(entity.to_xml.to_xml).to eq(comment_xml_bob_with_new_data.strip)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -229,50 +133,25 @@ module DiasporaFederation
|
|||
expect_callback(:fetch_related_entity, "Post", parent_guid).and_return(parent)
|
||||
end
|
||||
|
||||
it "parses legacy format" do
|
||||
xml = Nokogiri::XML(legacy_format_comment_xml_bob).root
|
||||
entity = Salmon::XmlPayload.unpack(xml)
|
||||
|
||||
expect(entity.author).to eq(author)
|
||||
expect(entity.text).to eq(text)
|
||||
end
|
||||
|
||||
it "parses legacy order with new xml format" do
|
||||
xml = Nokogiri::XML(legacy_order_new_format_comment_xml_bob).root
|
||||
entity = Salmon::XmlPayload.unpack(xml)
|
||||
|
||||
expect(entity.author).to eq(author)
|
||||
expect(entity.text).to eq(text)
|
||||
end
|
||||
|
||||
it "parses new order with legacy xml format" do
|
||||
xml = Nokogiri::XML(new_order_legacy_format_comment_xml_bob).root
|
||||
entity = Salmon::XmlPayload.unpack(xml)
|
||||
xml = Nokogiri::XML(comment_xml_bob_legacy_order).root
|
||||
entity = Entity.entity_class(xml.name).from_xml(xml)
|
||||
|
||||
expect(entity.author).to eq(author)
|
||||
expect(entity.text).to eq(text)
|
||||
end
|
||||
|
||||
it "parses new xml format" do
|
||||
xml = Nokogiri::XML(new_format_comment_xml_bob).root
|
||||
entity = Salmon::XmlPayload.unpack(xml)
|
||||
xml = Nokogiri::XML(comment_xml_bob).root
|
||||
entity = Entity.entity_class(xml.name).from_xml(xml)
|
||||
|
||||
expect(entity.author).to eq(author)
|
||||
expect(entity.text).to eq(text)
|
||||
end
|
||||
|
||||
it "parses new data with legacy xml format" do
|
||||
xml = Nokogiri::XML(legacy_format_new_data_comment_xml_bob).root
|
||||
entity = Salmon::XmlPayload.unpack(xml)
|
||||
|
||||
expect(entity.author).to eq(author)
|
||||
expect(entity.text).to eq(text)
|
||||
expect(entity.additional_data["new_data"]).to eq(new_data)
|
||||
end
|
||||
|
||||
it "parses new data with new xml format" do
|
||||
xml = Nokogiri::XML(new_data_comment_xml_bob).root
|
||||
entity = Salmon::XmlPayload.unpack(xml)
|
||||
xml = Nokogiri::XML(comment_xml_bob_with_new_data).root
|
||||
entity = Entity.entity_class(xml.name).from_xml(xml)
|
||||
|
||||
expect(entity.author).to eq(author)
|
||||
expect(entity.text).to eq(text)
|
||||
|
|
|
|||
|
|
@ -215,7 +215,8 @@ module DiasporaFederation
|
|||
|
||||
it "fails validation on parsing" do
|
||||
expect {
|
||||
DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML(xml).root)
|
||||
parsed_xml = Nokogiri::XML(xml).root
|
||||
Entity.entity_class(parsed_xml.name).from_xml(parsed_xml)
|
||||
}.to raise_error Entity::ValidationError
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ module DiasporaFederation
|
|||
<created_at>#{data[:created_at].utc.iso8601}</created_at>
|
||||
<edited_at>#{data[:edited_at].utc.iso8601}</edited_at>
|
||||
<author_signature>#{data[:author_signature]}</author_signature>
|
||||
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
|
||||
</comment>
|
||||
XML
|
||||
|
||||
|
|
|
|||
|
|
@ -54,11 +54,12 @@ module DiasporaFederation
|
|||
<guid>#{parent.guid}</guid>
|
||||
<subject>#{data[:subject]}</subject>
|
||||
<created_at>#{data[:created_at]}</created_at>
|
||||
<participant_handles>#{data[:participants]}</participant_handles>
|
||||
<participants>#{data[:participants]}</participants>
|
||||
</conversation>
|
||||
XML
|
||||
|
||||
parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML(minimal_xml).root)
|
||||
parsed_xml = Nokogiri::XML(minimal_xml).root
|
||||
parsed_instance = Entity.entity_class(parsed_xml.name).from_xml(parsed_xml)
|
||||
expect(parsed_instance.messages).to eq([])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ module DiasporaFederation
|
|||
<status>#{data[:status]}</status>
|
||||
<edited_at>#{data[:edited_at].utc.iso8601}</edited_at>
|
||||
<author_signature>#{data[:author_signature]}</author_signature>
|
||||
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
|
||||
</event_participation>
|
||||
XML
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ module DiasporaFederation
|
|||
</event>
|
||||
XML
|
||||
|
||||
parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML(minimal_xml).root)
|
||||
parsed_xml = Nokogiri::XML(minimal_xml).root
|
||||
parsed_instance = Entity.entity_class(parsed_xml.name).from_xml(parsed_xml)
|
||||
expect(parsed_instance.end).to be_nil
|
||||
expect(parsed_instance.all_day).to be_falsey
|
||||
expect(parsed_instance.timezone).to be_nil
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ module DiasporaFederation
|
|||
<parent_type>#{parent.entity_type}</parent_type>
|
||||
<positive>#{data[:positive]}</positive>
|
||||
<author_signature>#{data[:author_signature]}</author_signature>
|
||||
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
|
||||
</like>
|
||||
XML
|
||||
|
||||
|
|
@ -75,7 +74,7 @@ module DiasporaFederation
|
|||
it "raises a ValidationError if the parent_guid is missing" do
|
||||
broken_xml = <<~XML
|
||||
<like>
|
||||
<target_type>#{parent.entity_type}</target_type>
|
||||
<parent_type>#{parent.entity_type}</parent_type>
|
||||
</like>
|
||||
XML
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@ module DiasporaFederation
|
|||
</photo>
|
||||
XML
|
||||
|
||||
parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML(minimal_xml).root)
|
||||
parsed_xml = Nokogiri::XML(minimal_xml).root
|
||||
parsed_instance = Entity.entity_class(parsed_xml.name).from_xml(parsed_xml)
|
||||
expect(parsed_instance.public).to be_falsey
|
||||
expect(parsed_instance.text).to be_nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ module DiasporaFederation
|
|||
<parent_guid>#{parent.guid}</parent_guid>
|
||||
<poll_answer_guid>#{data[:poll_answer_guid]}</poll_answer_guid>
|
||||
<author_signature>#{data[:author_signature]}</author_signature>
|
||||
<parent_author_signature>#{data[:parent_author_signature]}</parent_author_signature>
|
||||
</poll_participation>
|
||||
XML
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ module DiasporaFederation
|
|||
</profile>
|
||||
XML
|
||||
|
||||
parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML(minimal_xml).root)
|
||||
parsed_xml = Nokogiri::XML(minimal_xml).root
|
||||
parsed_instance = Entity.entity_class(parsed_xml.name).from_xml(parsed_xml)
|
||||
expect(parsed_instance.full_name).to be_nil
|
||||
expect(parsed_instance.first_name).to be_nil
|
||||
expect(parsed_instance.last_name).to be_nil
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiasporaFederation
|
||||
describe Entities::RelayableRetraction do
|
||||
let(:target) { Fabricate(:comment, author: bob) }
|
||||
let(:target_entity) {
|
||||
Fabricate(
|
||||
:related_entity,
|
||||
author: bob.diaspora_id,
|
||||
parent: Fabricate(:related_entity, author: alice.diaspora_id)
|
||||
)
|
||||
}
|
||||
let(:data) { {author: alice.diaspora_id, target_guid: target.guid, target_type: target.entity_type} }
|
||||
|
||||
let(:xml) { <<~XML }
|
||||
<relayable_retraction>
|
||||
<parent_author_signature/>
|
||||
<target_guid>#{data[:target_guid]}</target_guid>
|
||||
<target_type>#{data[:target_type]}</target_type>
|
||||
<sender_handle>#{data[:author]}</sender_handle>
|
||||
<target_author_signature/>
|
||||
</relayable_retraction>
|
||||
XML
|
||||
|
||||
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(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
|
||||
end
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
module DiasporaFederation
|
||||
describe Entities::Relayable do
|
||||
let(:author_pkey) { OpenSSL::PKey::RSA.generate(1024) }
|
||||
let(:parent_pkey) { OpenSSL::PKey::RSA.generate(1024) }
|
||||
|
||||
let(: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(: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_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_data) { "#{author};#{guid};#{parent_guid};#{property}" }
|
||||
|
||||
describe "#initialize" 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)
|
||||
.to eq([:author, :guid, :parent_guid, :property, "new_property"])
|
||||
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
|
||||
|
||||
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[:parent_author_signature] = sign_with_key(parent_pkey, signature_data)
|
||||
hash[:parent] = remote_parent
|
||||
|
||||
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
|
||||
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_data_with_new_property = "#{author};#{guid};#{parent_guid};#{property};#{new_property}"
|
||||
|
||||
hash[:author_signature] = sign_with_key(author_pkey, signature_data_with_new_property)
|
||||
hash[:parent_author_signature] = sign_with_key(parent_pkey, signature_data_with_new_property)
|
||||
hash[:parent] = remote_parent
|
||||
|
||||
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
|
||||
}.to raise_error Entities::Relayable::SignatureVerificationFailed
|
||||
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
|
||||
|
||||
describe "#to_xml" do
|
||||
|
|
@ -130,7 +116,6 @@ module DiasporaFederation
|
|||
<property>#{property}</property>
|
||||
<new_property>#{new_property}</new_property>
|
||||
<author_signature>aa</author_signature>
|
||||
<parent_author_signature>bb</parent_author_signature>
|
||||
</some_relayable>
|
||||
XML
|
||||
|
||||
|
|
@ -169,7 +154,6 @@ module DiasporaFederation
|
|||
<property/>
|
||||
<new_property>#{new_property}</new_property>
|
||||
<author_signature>aa</author_signature>
|
||||
<parent_author_signature>bb</parent_author_signature>
|
||||
</some_relayable>
|
||||
XML
|
||||
|
||||
|
|
@ -188,7 +172,6 @@ module DiasporaFederation
|
|||
<guid>#{guid}</guid>
|
||||
<parent_guid>#{parent_guid}</parent_guid>
|
||||
<author_signature>aa</author_signature>
|
||||
<parent_author_signature>bb</parent_author_signature>
|
||||
</some_relayable>
|
||||
XML
|
||||
|
||||
|
|
@ -197,22 +180,16 @@ module DiasporaFederation
|
|||
expect(xml.to_s.strip).to eq(expected_xml.strip)
|
||||
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, local_parent.author).and_return(parent_pkey)
|
||||
|
||||
xml = Entities::SomeRelayable.new(hash).to_xml
|
||||
|
||||
author_signature = xml.at_xpath("author_signature").text
|
||||
parent_author_signature = xml.at_xpath("parent_author_signature").text
|
||||
|
||||
expect(verify_signature(author_pkey, author_signature, signature_data)).to be_truthy
|
||||
expect(verify_signature(parent_pkey, parent_author_signature, signature_data)).to be_truthy
|
||||
expect(verify_signature(author_pkey, xml.at_xpath("author_signature").text, signature_data)).to be_truthy
|
||||
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, local_parent.author).and_return(parent_pkey)
|
||||
|
||||
invalid_property = "asdfasdf 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
|
||||
|
||||
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(parent_pkey, parent_author_signature, signature_data_with_fixed_property)).to be_truthy
|
||||
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)
|
||||
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, local_parent.author).and_return(parent_pkey)
|
||||
expect(DiasporaFederation.callbacks).not_to receive(:trigger).with(:fetch_private_key, intermediate_author)
|
||||
|
||||
xml = Entities::SomeRelayable.new(hash.merge(parent: parent)).to_xml
|
||||
|
||||
author_signature = xml.at_xpath("author_signature").text
|
||||
parent_author_signature = xml.at_xpath("parent_author_signature").text
|
||||
|
||||
expect(verify_signature(author_pkey, author_signature, signature_data)).to be_truthy
|
||||
expect(verify_signature(parent_pkey, parent_author_signature, signature_data)).to be_truthy
|
||||
expect(verify_signature(author_pkey, xml.at_xpath("author_signature").text, signature_data)).to be_truthy
|
||||
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, local_parent.author).and_return(parent_pkey)
|
||||
|
||||
signature_order = [: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
|
||||
|
||||
author_signature = xml.at_xpath("author_signature").text
|
||||
parent_author_signature = xml.at_xpath("parent_author_signature").text
|
||||
|
||||
expect(verify_signature(author_pkey, author_signature, signature_data_with_new_property)).to be_truthy
|
||||
expect(verify_signature(parent_pkey, parent_author_signature, signature_data_with_new_property)).to be_truthy
|
||||
end
|
||||
|
||||
it "doesn't change signatures if they are already set" do
|
||||
it "doesn't change author_signature if it is already set" do
|
||||
xml = Entities::SomeRelayable.new(hash_with_fake_signatures).to_xml
|
||||
|
||||
expect(xml.at_xpath("author_signature").text).to eq("aa")
|
||||
expect(xml.at_xpath("parent_author_signature").text).to eq("bb")
|
||||
end
|
||||
|
||||
it "raises when author_signature not set and key isn't supplied" do
|
||||
|
|
@ -273,15 +237,6 @@ module DiasporaFederation
|
|||
}.to raise_error Entities::Relayable::AuthorPrivateKeyNotFound
|
||||
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
|
||||
expected_xml = <<~XML
|
||||
<test_relayable_with_boolean>
|
||||
|
|
@ -290,7 +245,6 @@ module DiasporaFederation
|
|||
<parent_guid>#{parent_guid}</parent_guid>
|
||||
<test>false</test>
|
||||
<author_signature>aa</author_signature>
|
||||
<parent_author_signature>bb</parent_author_signature>
|
||||
</test_relayable_with_boolean>
|
||||
XML
|
||||
|
||||
|
|
@ -310,13 +264,12 @@ module DiasporaFederation
|
|||
let(:new_signature_data) { "#{author};#{guid};#{parent_guid};#{new_property};#{property}" }
|
||||
let(:new_xml) { <<~XML }
|
||||
<some_relayable>
|
||||
<diaspora_handle>#{author}</diaspora_handle>
|
||||
<author>#{author}</author>
|
||||
<guid>#{guid}</guid>
|
||||
<parent_guid>#{parent_guid}</parent_guid>
|
||||
<new_property>#{new_property}</new_property>
|
||||
<property>#{property}</property>
|
||||
<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>
|
||||
XML
|
||||
|
||||
|
|
@ -338,7 +291,6 @@ module DiasporaFederation
|
|||
|
||||
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[:parent_author_signature] = sign_with_key(parent_pkey, signature_data)
|
||||
|
||||
xml = Entities::SomeRelayable.new(hash).to_xml
|
||||
|
||||
|
|
@ -450,13 +402,6 @@ module DiasporaFederation
|
|||
Entities::SomeRelayable.new(hash).to_json
|
||||
}.to raise_error Entities::Relayable::AuthorPrivateKeyNotFound
|
||||
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
|
||||
|
||||
describe ".from_hash" do
|
||||
|
|
@ -471,16 +416,14 @@ module DiasporaFederation
|
|||
context "when properties are sorted and there is an unknown property" do
|
||||
let(:new_signature_data) { "#{author};#{guid};#{parent_guid};#{new_property};#{property}" }
|
||||
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) {
|
||||
{
|
||||
:guid => guid,
|
||||
:author => author,
|
||||
:property => property,
|
||||
:parent_guid => parent_guid,
|
||||
"new_property" => new_property,
|
||||
:author_signature => author_signature,
|
||||
:parent_author_signature => parent_author_signature
|
||||
:guid => guid,
|
||||
:author => author,
|
||||
:property => property,
|
||||
:parent_guid => parent_guid,
|
||||
"new_property" => new_property,
|
||||
:author_signature => author_signature
|
||||
}
|
||||
}
|
||||
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.property).to eq(property)
|
||||
expect(entity.author_signature).to eq(author_signature)
|
||||
expect(entity.parent_author_signature).to eq(parent_author_signature)
|
||||
end
|
||||
|
||||
it "makes unknown properties available via #additional_data" do
|
||||
|
|
@ -509,13 +451,12 @@ module DiasporaFederation
|
|||
it "calls a constructor of the entity of the appropriate type" do
|
||||
expect(Entities::SomeRelayable).to receive(:new).with(
|
||||
{
|
||||
author: author,
|
||||
guid: guid,
|
||||
parent_guid: parent_guid,
|
||||
property: property,
|
||||
author_signature: author_signature,
|
||||
parent_author_signature: parent_author_signature,
|
||||
parent: remote_parent
|
||||
author: author,
|
||||
guid: guid,
|
||||
parent_guid: parent_guid,
|
||||
property: property,
|
||||
author_signature: author_signature,
|
||||
parent: remote_parent
|
||||
}.merge("new_property" => new_property),
|
||||
%w[author guid parent_guid new_property property],
|
||||
"new_property" => new_property
|
||||
|
|
@ -528,12 +469,11 @@ module DiasporaFederation
|
|||
property_order = %w[author guid parent_guid property]
|
||||
|
||||
entity_data = {
|
||||
guid: guid,
|
||||
author: author,
|
||||
property: property,
|
||||
parent_guid: parent_guid,
|
||||
author_signature: sign_with_key(author_pkey, signature_data),
|
||||
parent_author_signature: sign_with_key(parent_pkey, signature_data)
|
||||
guid: guid,
|
||||
author: author,
|
||||
property: property,
|
||||
parent_guid: parent_guid,
|
||||
author_signature: sign_with_key(author_pkey, signature_data)
|
||||
}
|
||||
|
||||
entity = Entities::SomeRelayable.from_hash(entity_data, property_order)
|
||||
|
|
@ -547,12 +487,11 @@ module DiasporaFederation
|
|||
it "calls signatures verification on relayable unpack" do
|
||||
property_order = %w[guid author property parent_guid]
|
||||
entity_data = {
|
||||
guid: guid,
|
||||
author: author,
|
||||
property: property,
|
||||
parent_guid: parent_guid,
|
||||
author_signature: "aa",
|
||||
parent_author_signature: "bb"
|
||||
guid: guid,
|
||||
author: author,
|
||||
property: property,
|
||||
parent_guid: parent_guid,
|
||||
author_signature: "aa"
|
||||
}
|
||||
|
||||
expect_callback(:fetch_related_entity, "Parent", parent_guid).and_return(remote_parent)
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiasporaFederation
|
||||
describe Entities::Request do
|
||||
let(:data) { {author: Fabricate.sequence(:diaspora_id), recipient: Fabricate.sequence(:diaspora_id)} }
|
||||
|
||||
let(:xml) { <<~XML }
|
||||
<request>
|
||||
<sender_handle>#{data[:author]}</sender_handle>
|
||||
<recipient_handle>#{data[:recipient]}</recipient_handle>
|
||||
</request>
|
||||
XML
|
||||
|
||||
describe "#initialize" do
|
||||
it "raises because it is not supported anymore" do
|
||||
expect {
|
||||
Entities::Request.new(data)
|
||||
}.to raise_error RuntimeError, "Sending Request is not supported anymore! Use Contact instead!"
|
||||
end
|
||||
end
|
||||
|
||||
context "parse contact" do
|
||||
it "parses the xml as a contact" do
|
||||
contact = Entities::Request.from_xml(Nokogiri::XML(xml).root)
|
||||
expect(contact).to be_a(Entities::Contact)
|
||||
expect(contact.author).to eq(data[:author])
|
||||
expect(contact.recipient).to eq(data[:recipient])
|
||||
expect(contact.following).to be_truthy
|
||||
expect(contact.sharing).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiasporaFederation
|
||||
describe Entities::SignedRetraction do
|
||||
let(:target) { Fabricate(:post, author: alice) }
|
||||
let(:target_entity) { Fabricate(:related_entity, author: alice.diaspora_id) }
|
||||
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>
|
||||
<sender_handle>#{data[:author]}</sender_handle>
|
||||
<target_author_signature/>
|
||||
</signed_retraction>
|
||||
XML
|
||||
|
||||
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(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
|
||||
end
|
||||
|
|
@ -136,7 +136,8 @@ module DiasporaFederation
|
|||
</status_message>
|
||||
XML
|
||||
|
||||
parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML(minimal_xml).root)
|
||||
parsed_xml = Nokogiri::XML(minimal_xml).root
|
||||
parsed_instance = Entity.entity_class(parsed_xml.name).from_xml(parsed_xml)
|
||||
expect(parsed_instance.photos).to eq([])
|
||||
expect(parsed_instance.location).to be_nil
|
||||
expect(parsed_instance.poll).to be_nil
|
||||
|
|
|
|||
|
|
@ -528,14 +528,5 @@ module DiasporaFederation
|
|||
expect(entity.multi).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "xml_name" do
|
||||
let(:hash) { {test: "test", qwer: "qwer"} }
|
||||
|
||||
it "should not use the xml_name for the #to_h" do
|
||||
entity = Entities::TestEntityWithXmlName.new(hash)
|
||||
expect(entity.to_h).to eq(hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,21 +23,6 @@ module DiasporaFederation
|
|||
described_class.receive_public(data)
|
||||
end
|
||||
|
||||
it "parses the entity with legacy slap receiver" do
|
||||
expect_callback(:fetch_public_key, post.author).and_return(sender_key)
|
||||
|
||||
data = generate_legacy_salmon_slap(post, post.author, sender_key)
|
||||
|
||||
expect_callback(:receive_entity, kind_of(Entities::StatusMessage), post.author, nil) do |_, entity|
|
||||
expect(entity.guid).to eq(post.guid)
|
||||
expect(entity.author).to eq(post.author)
|
||||
expect(entity.text).to eq(post.text)
|
||||
expect(entity.public).to eq("true")
|
||||
end
|
||||
|
||||
described_class.receive_public(data, true)
|
||||
end
|
||||
|
||||
it "redirects exceptions from the receiver" do
|
||||
expect {
|
||||
described_class.receive_public("<xml/>")
|
||||
|
|
@ -64,21 +49,6 @@ module DiasporaFederation
|
|||
described_class.receive_private(data, recipient_key, 1234)
|
||||
end
|
||||
|
||||
it "parses the entity with legacy slap receiver" do
|
||||
expect_callback(:fetch_public_key, post.author).and_return(sender_key)
|
||||
|
||||
data = generate_legacy_encrypted_salmon_slap(post, post.author, sender_key, recipient_key)
|
||||
|
||||
expect_callback(:receive_entity, kind_of(Entities::StatusMessage), post.author, 1234) do |_, entity|
|
||||
expect(entity.guid).to eq(post.guid)
|
||||
expect(entity.author).to eq(post.author)
|
||||
expect(entity.text).to eq(post.text)
|
||||
expect(entity.public).to eq("false")
|
||||
end
|
||||
|
||||
described_class.receive_private(data, recipient_key, 1234, true)
|
||||
end
|
||||
|
||||
it "raises when recipient private key is not available" do
|
||||
magic_env = Salmon::MagicEnvelope.new(post, post.author).envelop(sender_key)
|
||||
data = Salmon::EncryptedMagicEnvelope.encrypt(magic_env, recipient_key.public_key)
|
||||
|
|
|
|||
|
|
@ -33,34 +33,6 @@ module DiasporaFederation
|
|||
end
|
||||
end
|
||||
|
||||
it "uses xml_name for parsing" do
|
||||
xml = <<~XML.strip
|
||||
<test_entity_with_xml_name>
|
||||
<test>asdf</test>
|
||||
<asdf>qwer</asdf>
|
||||
</test_entity_with_xml_name>
|
||||
XML
|
||||
|
||||
parsed = Parsers::XmlParser.new(Entities::TestEntityWithXmlName).parse(Nokogiri::XML(xml).root)
|
||||
|
||||
expect(parsed[0][:test]).to eq("asdf")
|
||||
expect(parsed[0][:qwer]).to eq("qwer")
|
||||
end
|
||||
|
||||
it "allows name for parsing even when property has a xml_name" do
|
||||
xml = <<~XML.strip
|
||||
<test_entity_with_xml_name>
|
||||
<test>asdf</test>
|
||||
<qwer>qwer</qwer>
|
||||
</test_entity_with_xml_name>
|
||||
XML
|
||||
|
||||
parsed = Parsers::XmlParser.new(Entities::TestEntityWithXmlName).parse(Nokogiri::XML(xml).root)
|
||||
|
||||
expect(parsed[0][:test]).to eq("asdf")
|
||||
expect(parsed[0][:qwer]).to eq("qwer")
|
||||
end
|
||||
|
||||
it "parses the string to the correct type" do
|
||||
xml = <<~XML.strip
|
||||
<test_default_entity>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ module DiasporaFederation
|
|||
properties = dsl.class_props
|
||||
expect(properties).to have(1).item
|
||||
expect(properties[:test]).to eq(:string)
|
||||
expect(dsl.xml_names[:test]).to eq(:test)
|
||||
end
|
||||
|
||||
it "will not accept other types for names" do
|
||||
|
|
@ -46,22 +45,6 @@ module DiasporaFederation
|
|||
expect(properties.keys).to include(:test, :asdf, :zzzz)
|
||||
properties.each_value {|type| expect(type).to eq(:string) }
|
||||
end
|
||||
|
||||
it "can add an xml name to simple properties with a symbol" do
|
||||
dsl.property :test, :string, xml_name: :xml_test
|
||||
properties = dsl.class_props
|
||||
expect(properties).to have(1).item
|
||||
expect(properties[:test]).to eq(:string)
|
||||
expect(dsl.xml_names[:test]).to eq(:xml_test)
|
||||
end
|
||||
|
||||
it "will not accept other types for xml names" do
|
||||
["test", 1234, true, {}].each do |val|
|
||||
expect {
|
||||
dsl.property :test, :string, xml_name: val
|
||||
}.to raise_error PropertiesDSL::InvalidName, "invalid xml_name"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "nested entities" do
|
||||
|
|
@ -99,12 +82,6 @@ module DiasporaFederation
|
|||
}.to raise_error PropertiesDSL::InvalidType
|
||||
end
|
||||
end
|
||||
|
||||
it "can not add an xml name to a nested entity" do
|
||||
expect {
|
||||
dsl.entity :other, Entities::TestEntity, xml_name: :other_name
|
||||
}.to raise_error ArgumentError, "xml_name is not supported for nested entities"
|
||||
end
|
||||
end
|
||||
|
||||
describe ".optional_props" do
|
||||
|
|
@ -174,22 +151,5 @@ module DiasporaFederation
|
|||
expect(data).not_to have_key(:test_alias)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".find_property_for_xml_name" do
|
||||
it "finds property by xml_name" do
|
||||
dsl.property :test, :string, xml_name: :xml_test
|
||||
expect(dsl.find_property_for_xml_name("xml_test")).to eq(:test)
|
||||
end
|
||||
|
||||
it "finds property by name" do
|
||||
dsl.property :test, :string, xml_name: :xml_test
|
||||
expect(dsl.find_property_for_xml_name("test")).to eq(:test)
|
||||
end
|
||||
|
||||
it "returns nil if property is not defined" do
|
||||
dsl.property :test, :string, xml_name: :xml_test
|
||||
expect(dsl.find_property_for_xml_name("unknown")).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiasporaFederation
|
||||
describe Salmon::EncryptedSlap do
|
||||
let(:sender) { "user_test@diaspora.example.tld" }
|
||||
let(:privkey) { OpenSSL::PKey::RSA.generate(512) } # use small key for speedy specs
|
||||
let(:recipient_key) { OpenSSL::PKey::RSA.generate(1024) } # use small key for speedy specs
|
||||
let(:payload) { Entities::TestEntity.new(test: "qwertzuiop") }
|
||||
let(:slap_xml) { generate_legacy_encrypted_salmon_slap(payload, sender, privkey, recipient_key.public_key) }
|
||||
|
||||
describe ".from_xml" do
|
||||
context "sanity" do
|
||||
it "accepts correct params" do
|
||||
expect_callback(:fetch_public_key, sender).and_return(privkey.public_key)
|
||||
|
||||
expect {
|
||||
Salmon::EncryptedSlap.from_xml(slap_xml, recipient_key)
|
||||
}.not_to raise_error
|
||||
end
|
||||
|
||||
it "raises an error when the params have a wrong type" do
|
||||
[1234, false, :symbol, payload, privkey].each do |val|
|
||||
expect {
|
||||
Salmon::EncryptedSlap.from_xml(val, val)
|
||||
}.to raise_error ArgumentError
|
||||
end
|
||||
end
|
||||
|
||||
it "verifies the existence of 'encrypted_header'" do
|
||||
faulty_xml = <<~XML
|
||||
<diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env">
|
||||
</diaspora>
|
||||
XML
|
||||
expect {
|
||||
Salmon::EncryptedSlap.from_xml(faulty_xml, recipient_key)
|
||||
}.to raise_error Salmon::MissingHeader
|
||||
end
|
||||
|
||||
it "verifies the existence of a magic envelope" do
|
||||
faulty_xml = <<~XML
|
||||
<diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env">
|
||||
<encrypted_header/>
|
||||
</diaspora>
|
||||
XML
|
||||
expect(Salmon::EncryptedSlap).to receive(:header_data).and_return(aes_key: "", iv: "", author_id: "")
|
||||
expect {
|
||||
Salmon::EncryptedSlap.from_xml(faulty_xml, recipient_key)
|
||||
}.to raise_error Salmon::MissingMagicEnvelope
|
||||
end
|
||||
end
|
||||
|
||||
context "generated instance" do
|
||||
it_behaves_like "a MagicEnvelope instance" do
|
||||
subject { Salmon::EncryptedSlap.from_xml(slap_xml, recipient_key) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -16,6 +16,14 @@ module DiasporaFederation
|
|||
[data, type, enc, alg].map {|i| Base64.urlsafe_encode64(i) }.join(".")
|
||||
end
|
||||
|
||||
def encrypt_magic_env(magic_env)
|
||||
DiasporaFederation::Salmon::AES.generate_key_and_iv.tap do |key|
|
||||
magic_env.instance_variable_set(
|
||||
"@payload_data", DiasporaFederation::Salmon::AES.encrypt(magic_env.send(:payload_data), key[:key], key[:iv])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "sanity" do
|
||||
it "constructs an instance" do
|
||||
expect {
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiasporaFederation
|
||||
describe Salmon::Slap do
|
||||
let(:sender) { "test_user@pod.somedomain.tld" }
|
||||
let(:privkey) { OpenSSL::PKey::RSA.generate(512) } # use small key for speedy specs
|
||||
let(:payload) { Entities::TestEntity.new(test: "qwertzuiop") }
|
||||
let(:slap_xml) { generate_legacy_salmon_slap(payload, sender, privkey) }
|
||||
|
||||
describe ".from_xml" do
|
||||
context "sanity" do
|
||||
it "accepts salmon xml as param" do
|
||||
expect_callback(:fetch_public_key, sender).and_return(privkey.public_key)
|
||||
|
||||
expect {
|
||||
Salmon::Slap.from_xml(slap_xml)
|
||||
}.not_to raise_error
|
||||
end
|
||||
|
||||
it "raises an error when the param has a wrong type" do
|
||||
[1234, false, :symbol, payload, privkey].each do |val|
|
||||
expect {
|
||||
Salmon::Slap.from_xml(val)
|
||||
}.to raise_error ArgumentError
|
||||
end
|
||||
end
|
||||
|
||||
it "verifies the existence of an author_id" do
|
||||
faulty_xml = <<~XML
|
||||
<diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env">
|
||||
<header/>
|
||||
</diaspora>
|
||||
XML
|
||||
expect {
|
||||
Salmon::Slap.from_xml(faulty_xml)
|
||||
}.to raise_error Salmon::MissingAuthor
|
||||
end
|
||||
|
||||
it "verifies the existence of a magic envelope" do
|
||||
faulty_xml = <<~XML
|
||||
<diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env">
|
||||
<header>
|
||||
<author_id>#{sender}</author_id>
|
||||
</header>
|
||||
</diaspora>
|
||||
XML
|
||||
expect {
|
||||
Salmon::Slap.from_xml(faulty_xml)
|
||||
}.to raise_error Salmon::MissingMagicEnvelope
|
||||
end
|
||||
end
|
||||
|
||||
context "generated instance" do
|
||||
it_behaves_like "a MagicEnvelope instance" do
|
||||
subject { Salmon::Slap.from_xml(slap_xml) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiasporaFederation
|
||||
describe Salmon::XmlPayload do
|
||||
let(:entity) { Entities::TestEntity.new(test: "asdf") }
|
||||
let(:entity_xml) { <<~XML.strip }
|
||||
<XML>
|
||||
<post>
|
||||
<test_entity>
|
||||
<test>asdf</test>
|
||||
</test_entity>
|
||||
</post>
|
||||
</XML>
|
||||
XML
|
||||
|
||||
describe ".unpack" do
|
||||
context "sanity" do
|
||||
it "expects an Nokogiri::XML::Element as param" do
|
||||
expect {
|
||||
Salmon::XmlPayload.unpack(Nokogiri::XML(entity_xml).root)
|
||||
}.not_to raise_error
|
||||
end
|
||||
|
||||
it "raises and error when the param is not an Nokogiri::XML::Element" do
|
||||
["asdf", 1234, true, :test, entity].each do |val|
|
||||
expect {
|
||||
Salmon::XmlPayload.unpack(val)
|
||||
}.to raise_error ArgumentError, "only Nokogiri::XML::Element allowed"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "returned object" do
|
||||
subject { Salmon::XmlPayload.unpack(Nokogiri::XML(entity_xml).root) }
|
||||
|
||||
it "#to_h should match entity.to_h" do
|
||||
expect(subject.to_h).to eq(entity.to_h)
|
||||
end
|
||||
|
||||
it "returns an entity instance of the original class" do
|
||||
expect(subject).to be_an_instance_of Entities::TestEntity
|
||||
expect(subject.test).to eq("asdf")
|
||||
end
|
||||
|
||||
it "allows unwrapped entities" do
|
||||
xml = <<~XML
|
||||
<test_entity>
|
||||
<test>asdf</test>
|
||||
</test_entity>
|
||||
XML
|
||||
|
||||
entity = Salmon::XmlPayload.unpack(Nokogiri::XML(xml).root)
|
||||
|
||||
expect(entity).to be_an_instance_of Entities::TestEntity
|
||||
expect(entity.test).to eq("asdf")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -18,7 +18,6 @@ end
|
|||
def add_signatures(hash, klass=described_class)
|
||||
properties = klass.new(hash).send(:xml_elements)
|
||||
hash[:author_signature] = properties[:author_signature]
|
||||
hash[:parent_author_signature] = properties[:parent_author_signature]
|
||||
end
|
||||
|
||||
def sign_with_key(privkey, signature_data)
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This file only exists to generate legacy XMLs to test that we can still parse it.
|
||||
|
||||
def generate_legacy_salmon_slap(entity, sender, sender_privkey)
|
||||
build_salmon_slap_xml do |xml|
|
||||
xml.header {
|
||||
xml.author_id(sender)
|
||||
}
|
||||
|
||||
xml.parent << DiasporaFederation::Salmon::MagicEnvelope.new(entity, sender).envelop(sender_privkey).root
|
||||
end
|
||||
end
|
||||
|
||||
def generate_legacy_encrypted_salmon_slap(entity, sender, sender_privkey, recipient_pubkey)
|
||||
magic_envelope = DiasporaFederation::Salmon::MagicEnvelope.new(entity)
|
||||
cipher_params = encrypt_magic_env(magic_envelope)
|
||||
|
||||
build_salmon_slap_xml do |xml|
|
||||
xml.encrypted_header(encrypted_header(sender, cipher_params, recipient_pubkey))
|
||||
|
||||
xml.parent << magic_envelope.envelop(sender_privkey).root
|
||||
end
|
||||
end
|
||||
|
||||
def build_salmon_slap_xml
|
||||
Nokogiri::XML::Builder.new(encoding: "UTF-8") {|xml|
|
||||
xml.diaspora("xmlns" => DiasporaFederation::Salmon::XMLNS,
|
||||
"xmlns:me" => DiasporaFederation::Salmon::MagicEnvelope::XMLNS) {
|
||||
yield xml
|
||||
}
|
||||
}.to_xml
|
||||
end
|
||||
|
||||
def encrypt_magic_env(magic_env)
|
||||
DiasporaFederation::Salmon::AES.generate_key_and_iv.tap do |key|
|
||||
magic_env.instance_variable_set(
|
||||
"@payload_data", DiasporaFederation::Salmon::AES.encrypt(magic_env.send(:payload_data), key[:key], key[:iv])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def encrypted_header(author_id, envelope_key, pubkey)
|
||||
data = decrypted_header_xml(author_id, strict_base64_encode(envelope_key))
|
||||
header_key = DiasporaFederation::Salmon::AES.generate_key_and_iv
|
||||
ciphertext = DiasporaFederation::Salmon::AES.encrypt(data, header_key[:key], header_key[:iv])
|
||||
|
||||
json_key = JSON.generate(strict_base64_encode(header_key))
|
||||
encrypted_key = Base64.strict_encode64(pubkey.public_encrypt(json_key))
|
||||
|
||||
json_header = JSON.generate(aes_key: encrypted_key, ciphertext: ciphertext)
|
||||
|
||||
Base64.strict_encode64(json_header)
|
||||
end
|
||||
|
||||
def decrypted_header_xml(author_id, envelope_key)
|
||||
Nokogiri::XML::Builder.new(encoding: "UTF-8") {|xml|
|
||||
xml.decrypted_header {
|
||||
xml.iv(envelope_key[:iv])
|
||||
xml.aes_key(envelope_key[:key])
|
||||
xml.author_id(author_id)
|
||||
}
|
||||
}.to_xml.strip
|
||||
end
|
||||
|
||||
def strict_base64_encode(hash)
|
||||
hash.map {|k, v| [k, Base64.strict_encode64(v)] }.to_h
|
||||
end
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
def entity_hash_from(hash)
|
||||
hash.delete(:parent_author_signature)
|
||||
hash.map {|key, value|
|
||||
if [String, TrueClass, FalseClass, Integer, NilClass].any? {|c| value.is_a? c }
|
||||
[key, value]
|
||||
|
|
@ -67,7 +66,7 @@ shared_examples "an XML Entity" do |ignored_props=[]|
|
|||
context "parsing" do
|
||||
it "reads its own output" do
|
||||
packed_xml = instance.to_xml
|
||||
parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(packed_xml)
|
||||
parsed_instance = DiasporaFederation::Entity.entity_class(packed_xml.name).from_xml(packed_xml)
|
||||
|
||||
check_entity(instance, parsed_instance, ignored_props)
|
||||
end
|
||||
|
|
@ -101,24 +100,19 @@ shared_examples "an XML Entity" do |ignored_props=[]|
|
|||
end
|
||||
|
||||
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
|
||||
def verify_signature(pubkey, signature, signed_string)
|
||||
pubkey.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(signature), signed_string)
|
||||
end
|
||||
|
||||
it "computes correct signatures for the entity" do
|
||||
order = described_class.class_props.keys - %i[author_signature parent_author_signature parent]
|
||||
it "computes correct author_signature for the entity" do
|
||||
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(";")
|
||||
|
||||
xml = instance.to_xml
|
||||
|
||||
author_signature = xml.at_xpath("author_signature").text
|
||||
parent_author_signature = xml.at_xpath("parent_author_signature").text
|
||||
|
||||
author_signature = instance.to_xml.at_xpath("author_signature").text
|
||||
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
|
||||
|
|
@ -182,7 +176,6 @@ shared_examples "a relayable JSON entity" do
|
|||
it "matches JSON schema with empty string signatures" do
|
||||
json = described_class.new(data).to_json
|
||||
json[:entity_data][:author_signature] = ""
|
||||
json[:entity_data][:parent_author_signature] = ""
|
||||
expect(json.to_json).to match_json_schema(:entity_schema)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue