diff --git a/lib/diaspora_federation/discovery/xrd_document.rb b/lib/diaspora_federation/discovery/xrd_document.rb index 55c1b4f..09b0721 100644 --- a/lib/diaspora_federation/discovery/xrd_document.rb +++ b/lib/diaspora_federation/discovery/xrd_document.rb @@ -135,7 +135,7 @@ module DiasporaFederation private_class_method def self.parse_xrd_document(xrd_doc) raise ArgumentError unless xrd_doc.instance_of?(String) - doc = Nokogiri::XML::Document.parse(xrd_doc) + doc = Nokogiri::XML(xrd_doc) raise InvalidDocument, "Not an XRD document" if !doc.root || doc.root.name != "XRD" doc end diff --git a/lib/diaspora_federation/entities/comment.rb b/lib/diaspora_federation/entities/comment.rb index 99c8319..652e265 100644 --- a/lib/diaspora_federation/entities/comment.rb +++ b/lib/diaspora_federation/entities/comment.rb @@ -4,10 +4,6 @@ module DiasporaFederation # # @see Validators::CommentValidator class Comment < Entity - # Old signature order - # @deprecated - LEGACY_SIGNATURE_ORDER = %i(guid parent_guid text author).freeze - # The {Comment} parent is a {Post} PARENT_TYPE = "Post".freeze @@ -21,6 +17,12 @@ module DiasporaFederation # Comment entity creation time # @return [Time] creation time property :created_at, :timestamp, default: -> { Time.now.utc } + + # Remove "created_at" when no order was received. + # @deprecated TODO: Remove this, this will break compatibility with pods older than 0.6.3.0. + def signature_order + @signature_order || super.tap {|order| order.delete(:created_at) } + end end end end diff --git a/lib/diaspora_federation/entities/event_participation.rb b/lib/diaspora_federation/entities/event_participation.rb index 4c2dc22..2785ee4 100644 --- a/lib/diaspora_federation/entities/event_participation.rb +++ b/lib/diaspora_federation/entities/event_participation.rb @@ -4,19 +4,11 @@ module DiasporaFederation # # @see Validators::EventParticipationValidator class EventParticipation < Entity - # Old signature order - # @deprecated - LEGACY_SIGNATURE_ORDER = %i(author guid parent_guid status).freeze - # The {EventParticipation} parent is an {Event} PARENT_TYPE = "Event".freeze include Relayable - # Redefine the author property without +diaspora_handle+ +xml_name+ - # @deprecated Can be removed after XMLs are generated with new names - property :author, :string - # @!attribute [r] status # The participation status of the user # "accepted", "declined" or "tentative" diff --git a/lib/diaspora_federation/entities/like.rb b/lib/diaspora_federation/entities/like.rb index 02324ca..b1b7b2e 100644 --- a/lib/diaspora_federation/entities/like.rb +++ b/lib/diaspora_federation/entities/like.rb @@ -4,24 +4,20 @@ module DiasporaFederation # # @see Validators::LikeValidator class Like < Entity - # Old signature order - # @deprecated - LEGACY_SIGNATURE_ORDER = %i(positive guid parent_type parent_guid author).freeze - include Relayable - # @!attribute [r] positive - # If +true+ set a like, if +false+, set a dislike (dislikes are currently not - # implemented in the diaspora* frontend). - # @return [Boolean] is it a like or a dislike - property :positive, :boolean - # @!attribute [r] parent_type # A string describing the type of the parent # 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 + + # @!attribute [r] positive + # If +true+ set a like, if +false+, set a dislike (dislikes are currently not + # implemented in the diaspora* frontend). + # @return [Boolean] is it a like or a dislike + property :positive, :boolean end end end diff --git a/lib/diaspora_federation/entities/message.rb b/lib/diaspora_federation/entities/message.rb index 00cccea..ad10c07 100644 --- a/lib/diaspora_federation/entities/message.rb +++ b/lib/diaspora_federation/entities/message.rb @@ -4,11 +4,17 @@ module DiasporaFederation # # @see Validators::MessageValidator class Message < Entity - # Old signature order - # @deprecated - LEGACY_SIGNATURE_ORDER = %i(guid parent_guid text created_at author conversation_guid).freeze + # @!attribute [r] author + # The diaspora* ID of the author + # @see Person#author + # @return [String] diaspora* ID + property :author, :string, xml_name: :diaspora_handle - include Relayable + # @!attribute [r] guid + # A random string of at least 16 chars + # @see Validation::Rule::Guid + # @return [String] guid + property :guid, :string # @!attribute [r] text # Text of the message composed by a user @@ -26,55 +32,9 @@ module DiasporaFederation # @return [String] conversation guid property :conversation_guid, :string - # It is only valid to receive a {Message} from the author itself, - # or from the author of the parent {Conversation} if the author signature is valid. - # @deprecated remove after {Message} doesn't include {Relayable} anymore - def sender_valid?(sender) - sender == author || (sender == parent_author && verify_author_signature) - end - - # @deprecated remove after {Message} doesn't include {Relayable} anymore - def to_h - super.tap {|hash| hash[:created_at] = created_at.utc.iso8601 } - end - - # @deprecated remove after {Message} doesn't include {Relayable} anymore - private_class_method def self.xml_parser_class - DiasporaFederation::Parsers::XmlParser - end - - # Default implementation, don't verify signatures for a {Message}. - # @see Entity.from_hash - # @deprecated remove after {Message} doesn't include {Relayable} anymore - # @param [Hash] properties hash - # @return [Entity] instance - def self.from_hash(hash) - new({parent_guid: nil, parent: nil}.merge(hash)) - end - - private - - # @deprecated remove after {Message} doesn't include {Relayable} anymore - def verify_author_signature - verify_signature(author, :author_signature) - true - end - - # @deprecated remove after {Message} doesn't include {Relayable} anymore - def parent_author - parent = DiasporaFederation.callbacks.trigger(:fetch_related_entity, "Conversation", conversation_guid) - raise Federation::Fetcher::NotFetchable, "parent of #{self} not found" unless parent - parent.author - end - - # old timestamp format, because this signature is only used from old pods which also relay with old format - # @deprecated remove after {Message} doesn't include {Relayable} anymore - def normalize_property(name, value) - if name == :created_at - value.to_s - else - super - end + # @return [String] string representation of this object + def to_s + "#{super}:#{conversation_guid}" end end end diff --git a/lib/diaspora_federation/entities/participation.rb b/lib/diaspora_federation/entities/participation.rb index d42fd53..d0cc815 100644 --- a/lib/diaspora_federation/entities/participation.rb +++ b/lib/diaspora_federation/entities/participation.rb @@ -4,11 +4,22 @@ module DiasporaFederation # # @see Validators::Participation class Participation < Entity - # Old signature order - # @deprecated - LEGACY_SIGNATURE_ORDER = %i(guid parent_type parent_guid author).freeze + # @!attribute [r] author + # The diaspora* ID of the author + # @see Person#author + # @return [String] diaspora* ID + property :author, :string, xml_name: :diaspora_handle - include Relayable + # @!attribute [r] guid + # A random string of at least 16 chars + # @see Validation::Rule::Guid + # @return [String] guid + property :guid, :string + + # @!attribute [r] parent_guid + # @see StatusMessage#guid + # @return [String] parent guid + property :parent_guid, :string # @!attribute [r] parent_type # A string describing a type of the target to subscribe on @@ -16,18 +27,9 @@ module DiasporaFederation # @return [String] parent type property :parent_type, :string, xml_name: :target_type - # It is only valid to receive a {Participation} from the author themself. - # @deprecated remove after {Participation} doesn't include {Relayable} anymore - def sender_valid?(sender) - sender == author - end - - # hackaround hacky from_hash override - # @deprecated remove after {Participation} doesn't include {Relayable} anymore - def enriched_properties - super.tap {|hash| - hash.delete(:parent) if hash[:parent].nil? - } + # @return [String] string representation of this object + def to_s + "#{super}:#{parent_type}:#{parent_guid}" end # Validates that the parent exists and the parent author is local @@ -36,22 +38,12 @@ module DiasporaFederation raise ParentNotLocal, "obj=#{self}" unless parent && parent.local end - # Don't verify signatures for a {Participation}. Validate that the parent is local. + # Validate that the parent is local. # @see Entity.from_hash # @param [Hash] hash entity initialization hash # @return [Entity] instance def self.from_hash(hash) - new(hash.merge(parent: nil)).tap(&:validate_parent) - end - - # @deprecated remove after {Participation} doesn't include {Relayable} anymore - private_class_method def self.xml_parser_class - DiasporaFederation::Parsers::XmlParser - end - - # @deprecated remove after {Participation} doesn't include {Relayable} anymore - private_class_method def self.json_parser_class - DiasporaFederation::Parsers::JsonParser + super.tap(&:validate_parent) end # Raised, if the parent is not owned by the receiving pod. diff --git a/lib/diaspora_federation/entities/poll_participation.rb b/lib/diaspora_federation/entities/poll_participation.rb index 8ef8f9e..d1ce464 100644 --- a/lib/diaspora_federation/entities/poll_participation.rb +++ b/lib/diaspora_federation/entities/poll_participation.rb @@ -4,10 +4,6 @@ module DiasporaFederation # # @see Validators::PollParticipationValidator class PollParticipation < Entity - # Old signature order - # @deprecated - LEGACY_SIGNATURE_ORDER = %i(guid parent_guid author poll_answer_guid).freeze - # The {PollParticipation} parent is a {Poll} PARENT_TYPE = "Poll".freeze diff --git a/lib/diaspora_federation/entities/relayable.rb b/lib/diaspora_federation/entities/relayable.rb index ce30e69..2892f82 100644 --- a/lib/diaspora_federation/entities/relayable.rb +++ b/lib/diaspora_federation/entities/relayable.rb @@ -7,10 +7,6 @@ module DiasporaFederation module Relayable include Signable - # Order from the parsed xml for signature - # @return [Array] order from xml - attr_reader :xml_order - # Additional properties from parsed input object # @return [Hash] additional elements attr_reader :additional_data @@ -65,11 +61,11 @@ module DiasporaFederation # Initializes a new relayable Entity with order and additional xml elements # # @param [Hash] data entity data - # @param [Array] xml_order order from xml + # @param [Array] signature_order order for the signature # @param [Hash] additional_data additional xml elements # @see DiasporaFederation::Entity#initialize - def initialize(data, xml_order=nil, additional_data={}) - @xml_order = xml_order.reject {|name| name =~ /signature/ } if xml_order + def initialize(data, signature_order=nil, additional_data={}) + self.signature_order = signature_order if signature_order @additional_data = additional_data super(data) @@ -103,6 +99,12 @@ module DiasporaFederation } end + # The order for signing + # @return [Array] + def signature_order + @signature_order || self.class.class_props.keys - %i(author_signature parent_author_signature parent) + end + private # Sign with author key @@ -147,15 +149,10 @@ module DiasporaFederation order.map {|element| [element, data[element] || ""] }.to_h end - # The order for signing - # @return [Array] - def signature_order - if xml_order - prop_names = self.class.class_props.keys.map(&:to_s) - xml_order.map {|name| prop_names.include?(name) ? name.to_sym : name } - else - self.class::LEGACY_SIGNATURE_ORDER - end + def signature_order=(order) + prop_names = self.class.class_props.keys.map(&:to_s) + @signature_order = order.reject {|name| name =~ /signature/ } + .map {|name| prop_names.include?(name) ? name.to_sym : name } end # @return [String] signature data string diff --git a/lib/diaspora_federation/entities/relayable_retraction.rb b/lib/diaspora_federation/entities/relayable_retraction.rb index 85570ca..aa7b03f 100644 --- a/lib/diaspora_federation/entities/relayable_retraction.rb +++ b/lib/diaspora_federation/entities/relayable_retraction.rb @@ -53,50 +53,13 @@ module DiasporaFederation # @return [String] target author signature property :target_author_signature, :string, default: nil - # @!attribute [r] target - # Target entity - # @return [RelatedEntity] target entity - entity :target, Entities::RelatedEntity - - # Use only {Retraction} for receive - # @return [Retraction] instance as normal retraction - def to_retraction - Retraction.new(author: author, target_guid: target_guid, target_type: target_type, target: target) - end - - # @return [String] string representation of this object - def to_s - "RelayableRetraction:#{target_type}:#{target_guid}" + def initialize(*) + raise "Sending RelayableRetraction is not supported anymore! Use Retraction instead!" end # @return [Retraction] instance def self.from_hash(hash) - hash[:target] = Retraction.send(:fetch_target, hash[:target_type], hash[:target_guid]) - new(hash).to_retraction - end - - private - - # It updates also the signatures with the keys of the author and the parent - # if the signatures are not there yet and if the keys are available. - # - # @return [Hash] xml elements with updated signatures - def enriched_properties - privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key, author) - - super.tap do |hash| - fill_required_signature(privkey, hash) unless privkey.nil? - end - end - - # @param [OpenSSL::PKey::RSA] privkey private key of sender - # @param [Hash] hash hash given for a signing - def fill_required_signature(privkey, hash) - if target.parent.author == author && parent_author_signature.nil? - hash[:parent_author_signature] = SignedRetraction.sign_with_key(privkey, self) - elsif target.author == author && target_author_signature.nil? - hash[:target_author_signature] = SignedRetraction.sign_with_key(privkey, self) - end + Retraction.from_hash(hash) end end end diff --git a/lib/diaspora_federation/entities/request.rb b/lib/diaspora_federation/entities/request.rb index b8b44c4..a60aab1 100644 --- a/lib/diaspora_federation/entities/request.rb +++ b/lib/diaspora_federation/entities/request.rb @@ -18,20 +18,13 @@ module DiasporaFederation # @return [String] recipient ID property :recipient, :string, xml_name: :recipient_handle - # Use only {Contact} for receive - # @return [Contact] instance as contact - def to_contact - Contact.new(author: author, recipient: recipient) - end - - # @return [String] string representation of this object - def to_s - "Request:#{author}:#{recipient}" + def initialize(*) + raise "Sending Request is not supported anymore! Use Contact instead!" end # @return [Retraction] instance def self.from_hash(hash) - super.to_contact + Contact.new(hash) end end end diff --git a/lib/diaspora_federation/entities/signed_retraction.rb b/lib/diaspora_federation/entities/signed_retraction.rb index 389543e..af850b9 100644 --- a/lib/diaspora_federation/entities/signed_retraction.rb +++ b/lib/diaspora_federation/entities/signed_retraction.rb @@ -30,51 +30,13 @@ module DiasporaFederation # @return [String] author signature property :target_author_signature, :string, default: nil - # @!attribute [r] target - # Target entity - # @return [RelatedEntity] target entity - entity :target, Entities::RelatedEntity - - # Use only {Retraction} for receive - # @return [Retraction] instance as normal retraction - def to_retraction - Retraction.new(author: author, target_guid: target_guid, target_type: target_type, target: target) - end - - # Create signature for a retraction - # @param [OpenSSL::PKey::RSA] privkey private key of sender - # @param [SignedRetraction, RelayableRetraction] ret the retraction to sign - # @return [String] a Base64 encoded signature of the retraction with the key - def self.sign_with_key(privkey, ret) - Base64.strict_encode64(privkey.sign(Relayable::DIGEST, [ret.target_guid, ret.target_type].join(";"))) - end - - # @return [String] string representation of this object - def to_s - "SignedRetraction:#{target_type}:#{target_guid}" + def initialize(*) + raise "Sending SignedRetraction is not supported anymore! Use Retraction instead!" end # @return [Retraction] instance def self.from_hash(hash) - hash[:target] = Retraction.send(:fetch_target, hash[:target_type], hash[:target_guid]) - new(hash).to_retraction - end - - private - - # It updates also the signatures with the keys of the author and the parent - # if the signatures are not there yet and if the keys are available. - # - # @return [Hash] xml elements with updated signatures - def enriched_properties - super.tap do |hash| - hash[:target_author_signature] = target_author_signature || sign_with_author.to_s - end - end - - def sign_with_author - privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key, author) - SignedRetraction.sign_with_key(privkey, self) unless privkey.nil? + Retraction.from_hash(hash) end end end diff --git a/lib/diaspora_federation/entity.rb b/lib/diaspora_federation/entity.rb index 6d738d2..024a134 100644 --- a/lib/diaspora_federation/entity.rb +++ b/lib/diaspora_federation/entity.rb @@ -90,7 +90,6 @@ module DiasporaFederation # {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Element Nokogiri::XML::Element}s # # @see Nokogiri::XML::Node.to_xml - # @see XmlPayload#pack # # @return [Nokogiri::XML::Element] root element containing properties as child elements def to_xml @@ -296,8 +295,7 @@ module DiasporaFederation # Create simple node, fill it with text and append to root def simple_node(doc, name, value) - xml_name = self.class.xml_names[name] - Nokogiri::XML::Element.new(xml_name ? xml_name.to_s : name, doc).tap do |node| + Nokogiri::XML::Element.new(name.to_s, doc).tap do |node| node.content = value.gsub(INVALID_XML_REGEX, "\uFFFD") unless value.empty? end end diff --git a/lib/diaspora_federation/federation/fetcher.rb b/lib/diaspora_federation/federation/fetcher.rb index 83cc08f..bce97df 100644 --- a/lib/diaspora_federation/federation/fetcher.rb +++ b/lib/diaspora_federation/federation/fetcher.rb @@ -13,7 +13,7 @@ module DiasporaFederation response = HttpClient.get(url) raise "Failed to fetch #{url}: #{response.status}" unless response.success? - magic_env_xml = Nokogiri::XML::Document.parse(response.body).root + magic_env_xml = Nokogiri::XML(response.body).root magic_env = Salmon::MagicEnvelope.unenvelop(magic_env_xml) Receiver::Public.new(magic_env).receive rescue => e diff --git a/lib/diaspora_federation/federation/receiver.rb b/lib/diaspora_federation/federation/receiver.rb index cc4277f..bfe9436 100644 --- a/lib/diaspora_federation/federation/receiver.rb +++ b/lib/diaspora_federation/federation/receiver.rb @@ -11,7 +11,7 @@ module DiasporaFederation magic_env = if legacy Salmon::Slap.from_xml(data) else - magic_env_xml = Nokogiri::XML::Document.parse(data).root + magic_env_xml = Nokogiri::XML(data).root Salmon::MagicEnvelope.unenvelop(magic_env_xml) end Public.new(magic_env).receive diff --git a/lib/diaspora_federation/federation/sender.rb b/lib/diaspora_federation/federation/sender.rb index 63283e7..ab15662 100644 --- a/lib/diaspora_federation/federation/sender.rb +++ b/lib/diaspora_federation/federation/sender.rb @@ -11,7 +11,7 @@ module DiasporaFederation # @return [Array] url to retry def self.public(sender_id, obj_str, urls, xml) hydra = HydraWrapper.new(sender_id, obj_str) - urls.each {|url| hydra.insert_job(url, xml) } + urls.each {|url| hydra.insert_magic_env_request(url, xml) } hydra.send end @@ -23,7 +23,7 @@ module DiasporaFederation # @return [Hash] targets to retry def self.private(sender_id, obj_str, targets) hydra = HydraWrapper.new(sender_id, obj_str) - targets.each {|url, xml| hydra.insert_job(url, xml) } + targets.each {|url, json| hydra.insert_enc_magic_env_request(url, json) } hydra.send.map {|url| [url, targets[url]] }.to_h end end diff --git a/lib/diaspora_federation/federation/sender/hydra_wrapper.rb b/lib/diaspora_federation/federation/sender/hydra_wrapper.rb index ea25c82..be6dbae 100644 --- a/lib/diaspora_federation/federation/sender/hydra_wrapper.rb +++ b/lib/diaspora_federation/federation/sender/hydra_wrapper.rb @@ -17,12 +17,21 @@ module DiasporaFederation method: :post, verbose: DiasporaFederation.http_verbose, cainfo: DiasporaFederation.certificate_authorities, - forbid_reuse: true, - headers: { - "Expect" => "", - "Transfer-Encoding" => "", - "User-Agent" => DiasporaFederation.http_user_agent - } + forbid_reuse: true + } + end + + def self.xml_headers + @xml_headers ||= { + "Content-Type" => "application/magic-envelope+xml", + "User-Agent" => DiasporaFederation.http_user_agent + } + end + + def self.json_headers + @json_headers ||= { + "Content-Type" => "application/json", + "User-Agent" => DiasporaFederation.http_user_agent } end @@ -36,13 +45,18 @@ module DiasporaFederation @urls_to_retry = [] end - # Prepares and inserts job into the hydra queue + # Prepares and inserts a public MagicEnvelope job into the hydra queue # @param [String] url the receive-url for the xml - # @param [String] xml xml salmon message - def insert_job(url, xml) - request = Typhoeus::Request.new(url, HydraWrapper.hydra_opts.merge(body: {xml: xml})) - prepare_request(request) - hydra.queue(request) + # @param [String] xml MagicEnvelope xml + def insert_magic_env_request(url, xml) + insert_job(url, HydraWrapper.hydra_opts.merge(body: xml, headers: HydraWrapper.xml_headers)) + end + + # Prepares and inserts a private encrypted MagicEnvelope job into the hydra queue + # @param [String] url the receive-url for the message + # @param [String] json encrypted MagicEnvelope json + def insert_enc_magic_env_request(url, json) + insert_job(url, HydraWrapper.hydra_opts.merge(body: json, headers: HydraWrapper.json_headers)) end # Sends all queued messages @@ -54,6 +68,15 @@ module DiasporaFederation private + # Prepares and inserts job into the hydra queue + # @param [String] url the receive-url for the message + # @param [Hash] options request options + def insert_job(url, options) + request = Typhoeus::Request.new(url, options) + prepare_request(request) + hydra.queue(request) + end + # @return [Typhoeus::Hydra] hydra def hydra @hydra ||= Typhoeus::Hydra.new(max_concurrency: DiasporaFederation.http_concurrency) diff --git a/lib/diaspora_federation/salmon/encrypted_magic_envelope.rb b/lib/diaspora_federation/salmon/encrypted_magic_envelope.rb index de593ca..a883474 100644 --- a/lib/diaspora_federation/salmon/encrypted_magic_envelope.rb +++ b/lib/diaspora_federation/salmon/encrypted_magic_envelope.rb @@ -54,7 +54,7 @@ module DiasporaFederation key = encoded_key.map {|k, v| [k, Base64.decode64(v)] }.to_h xml = AES.decrypt(encrypted_json["encrypted_magic_envelope"], key["key"], key["iv"]) - Nokogiri::XML::Document.parse(xml).root + Nokogiri::XML(xml).root end end end diff --git a/lib/diaspora_federation/salmon/encrypted_slap.rb b/lib/diaspora_federation/salmon/encrypted_slap.rb index 4acc4ef..2fa772f 100644 --- a/lib/diaspora_federation/salmon/encrypted_slap.rb +++ b/lib/diaspora_federation/salmon/encrypted_slap.rb @@ -48,32 +48,12 @@ module DiasporaFederation # Finally, before decrypting the magic envelope payload, the signature should # first be verified. # - # @example Generating an encrypted Salmon Slap - # author_id = "author@pod.example.tld" - # author_privkey = however_you_retrieve_the_authors_private_key(author_id) - # recipient_pubkey = however_you_retrieve_the_recipients_public_key() - # entity = YourEntity.new(attr: "val") - # - # slap_xml = EncryptedSlap.prepare(author_id, author_privkey, entity).generate_xml(recipient_pubkey) - # # @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 - # the author of the slap - # @param [String] value the author diaspora* ID - attr_writer :author_id - - # the key and iv if it is an encrypted slap - # @param [Hash] value hash containing the key and iv - attr_writer :cipher_params - - # the prepared encrypted magic envelope xml - # @param [Nokogiri::XML::Element] value magic envelope xml - attr_writer :magic_envelope_xml - # Creates a {MagicEnvelope} instance from the data within the given XML string # containing an encrypted payload. # @@ -87,7 +67,7 @@ module DiasporaFederation # @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::Document.parse(slap_xml) + doc = Nokogiri::XML(slap_xml) header_elem = doc.at_xpath("d:diaspora/d:encrypted_header", Slap::NS) raise MissingHeader if header_elem.nil? @@ -98,44 +78,6 @@ module DiasporaFederation MagicEnvelope.unenvelop(magic_env_from_doc(doc), sender, cipher_params) end - # Creates an encrypted Salmon Slap. - # - # @param [String] author_id diaspora* ID of the author - # @param [OpenSSL::PKey::RSA] privkey sender private key for signing the magic envelope - # @param [Entity] entity payload - # @return [EncryptedSlap] encrypted Slap instance - # @raise [ArgumentError] if any of the arguments is of the wrong type - def self.prepare(author_id, privkey, entity) - raise ArgumentError unless author_id.instance_of?(String) && - privkey.instance_of?(OpenSSL::PKey::RSA) && - entity.is_a?(Entity) - - EncryptedSlap.new.tap do |slap| - slap.author_id = author_id - - magic_envelope = MagicEnvelope.new(entity) - slap.cipher_params = magic_envelope.encrypt! - slap.magic_envelope_xml = magic_envelope.envelop(privkey) - end - end - - # Creates an encrypted Salmon Slap XML string. - # - # @param [OpenSSL::PKey::RSA] pubkey recipient public key for encrypting the AES key - # @return [String] Salmon XML string - # @raise [ArgumentError] if any of the arguments is of the wrong type - def generate_xml(pubkey) - raise ArgumentError unless pubkey.instance_of?(OpenSSL::PKey::RSA) - - Slap.build_xml do |xml| - xml.encrypted_header(encrypted_header(@author_id, @cipher_params, pubkey)) - - xml.parent << @magic_envelope_xml - end - end - - private - # 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 @@ -160,46 +102,7 @@ module DiasporaFederation 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::Document.parse(xml).root - end - - # Encrypt the header xml with an AES cipher and encrypt the cipher params - # with the recipients public_key. - # @param [String] author_id diaspora_handle - # @param [Hash] envelope_key envelope cipher params - # @param [OpenSSL::PKey::RSA] pubkey recipient public_key - # @return [String] encrypted base64 encoded header - def encrypted_header(author_id, envelope_key, pubkey) - data = header_xml(author_id, strict_base64_encode(envelope_key)) - header_key = AES.generate_key_and_iv - ciphertext = 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 - - # Generate the header xml string, including the author, aes_key and iv - # @param [String] author_id diaspora_handle of the author - # @param [Hash] envelope_key { key: "...", iv: "..." } (values in base64) - # @return [String] header XML string - def header_xml(author_id, envelope_key) - @header_xml ||= 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 - - # @param [Hash] hash { key: "...", iv: "..." } - # @return [Hash] encoded hash: { key: "...", iv: "..." } - def strict_base64_encode(hash) - hash.map {|k, v| [k, Base64.strict_encode64(v)] }.to_h + Nokogiri::XML(xml).root end end end diff --git a/lib/diaspora_federation/salmon/magic_envelope.rb b/lib/diaspora_federation/salmon/magic_envelope.rb index be1979c..ac53e95 100644 --- a/lib/diaspora_federation/salmon/magic_envelope.rb +++ b/lib/diaspora_federation/salmon/magic_envelope.rb @@ -76,22 +76,6 @@ module DiasporaFederation } end - # Encrypts the payload with a new, random AES cipher and returns the cipher - # params that were used. - # - # This must happen after the MagicEnvelope instance was created and before - # {MagicEnvelope#envelop} is called. - # - # @see AES#generate_key_and_iv - # @see AES#encrypt - # - # @return [Hash] AES key and iv. E.g.: { key: "...", iv: "..." } - def encrypt! - AES.generate_key_and_iv.tap do |key| - @payload_data = AES.encrypt(payload_data, key[:key], key[:iv]) - end - end - # Extracts the entity encoded in the magic envelope data, if the signature # is valid. If +cipher_params+ is given, also attempts to decrypt the payload first. # @@ -128,7 +112,7 @@ module DiasporaFederation logger.debug "unenvelop message from #{sender}:\n#{data}" - new(XmlPayload.unpack(Nokogiri::XML::Document.parse(data).root), sender) + new(XmlPayload.unpack(Nokogiri::XML(data).root), sender) end private @@ -136,7 +120,7 @@ module DiasporaFederation # The payload data as string # @return [String] payload data def payload_data - @payload_data ||= XmlPayload.pack(@payload).to_xml.strip.tap do |data| + @payload_data ||= payload.to_xml.to_xml.strip.tap do |data| logger.debug "send payload:\n#{data}" end end @@ -153,7 +137,7 @@ module DiasporaFederation def build_xml Nokogiri::XML::Builder.new(encoding: "UTF-8") {|xml| yield xml - }.doc.root + }.doc end # Creates the signature for all fields according to specification diff --git a/lib/diaspora_federation/salmon/slap.rb b/lib/diaspora_federation/salmon/slap.rb index 958550a..f0daa92 100644 --- a/lib/diaspora_federation/salmon/slap.rb +++ b/lib/diaspora_federation/salmon/slap.rb @@ -13,13 +13,6 @@ module DiasporaFederation # {magic_envelope} # # - # @example Generating a Salmon Slap - # author_id = "author@pod.example.tld" - # author_privkey = however_you_retrieve_the_authors_private_key(author_id) - # entity = YourEntity.new(attr: "val") - # - # slap_xml = Slap.generate_xml(author_id, author_privkey, entity) - # # @example Parsing a Salmon Slap # entity = Slap.from_xml(slap_xml).payload # @@ -40,7 +33,7 @@ module DiasporaFederation # @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::Document.parse(slap_xml) + 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? @@ -49,40 +42,6 @@ module DiasporaFederation MagicEnvelope.unenvelop(magic_env_from_doc(doc), sender) end - # Creates an unencrypted Salmon Slap and returns the XML string. - # - # @param [String] author_id diaspora* ID of the author - # @param [OpenSSL::PKey::RSA] privkey sender private_key for signing the magic envelope - # @param [Entity] entity payload - # @return [String] Salmon XML string - # @raise [ArgumentError] if any of the arguments is not the correct type - def self.generate_xml(author_id, privkey, entity) - raise ArgumentError unless author_id.instance_of?(String) && - privkey.instance_of?(OpenSSL::PKey::RSA) && - entity.is_a?(Entity) - - build_xml do |xml| - xml.header { - xml.author_id(author_id) - } - - xml.parent << MagicEnvelope.new(entity, author_id).envelop(privkey) - end - end - - # Builds the xml for the Salmon Slap. - # - # @yield [xml] Invokes the block with the - # {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Builder Nokogiri::XML::Builder} - # @return [String] Slap XML - def self.build_xml - Nokogiri::XML::Builder.new(encoding: "UTF-8") {|xml| - xml.diaspora("xmlns" => Salmon::XMLNS, "xmlns:me" => MagicEnvelope::XMLNS) { - yield xml - } - }.to_xml - end - # Parses the magic envelop from the document. # # @param [Nokogiri::XML::Document] doc Salmon XML Document diff --git a/lib/diaspora_federation/salmon/xml_payload.rb b/lib/diaspora_federation/salmon/xml_payload.rb index 68c4f67..90d8557 100644 --- a/lib/diaspora_federation/salmon/xml_payload.rb +++ b/lib/diaspora_federation/salmon/xml_payload.rb @@ -13,25 +13,6 @@ module DiasporaFederation # (The +post+ element is there for historic reasons...) # @deprecated module XmlPayload - # Encapsulates an Entity inside the wrapping xml structure - # and returns the XML Object. - # - # @param [Entity] entity subject - # @return [Nokogiri::XML::Element] XML root node - # @raise [ArgumentError] if the argument is not an Entity subclass - def self.pack(entity) - raise ArgumentError, "only instances of DiasporaFederation::Entity allowed" unless entity.is_a?(Entity) - - entity_xml = entity.to_xml - doc = entity_xml.document - wrap = Nokogiri::XML::Element.new("XML", doc) - wrap_post = Nokogiri::XML::Element.new("post", doc) - entity_xml.parent = wrap_post - wrap << wrap_post - - wrap - end - # 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. diff --git a/lib/diaspora_federation/schemas/federation_entities.json b/lib/diaspora_federation/schemas/federation_entities.json index 98ec53a..a243efb 100644 --- a/lib/diaspora_federation/schemas/federation_entities.json +++ b/lib/diaspora_federation/schemas/federation_entities.json @@ -110,24 +110,22 @@ }, "participation": { - "allOf": [ - {"$ref": "#/definitions/relayable"}, - { + "type": "object", + "properties": { + "entity_type": { + "type": "string", + "pattern": "^participation$" + }, + "entity_data": { "type": "object", "properties": { - "entity_type": { - "type": "string", - "pattern": "^participation$" - }, - "entity_data": { - "type": "object", - "properties": { - "parent_type": {"enum": ["Post"]} - } - } + "author": { "type": "string" }, + "guid": { "$ref": "#/definitions/guid" }, + "parent_guid": { "$ref": "#/definitions/guid" }, + "parent_type": {"enum": ["Post"]} } } - ] + } }, "poll_participation": { diff --git a/lib/diaspora_federation/test/factories.rb b/lib/diaspora_federation/test/factories.rb index fbcc3ed..7000c89 100644 --- a/lib/diaspora_federation/test/factories.rb +++ b/lib/diaspora_federation/test/factories.rb @@ -93,10 +93,10 @@ module DiasporaFederation parent { Fabricate(:related_entity) } end - Fabricator(:participation_entity, - class_name: DiasporaFederation::Entities::Participation, from: :relayable_entity) do + Fabricator(:participation_entity, class_name: DiasporaFederation::Entities::Participation) do author { Fabricate.sequence(:diaspora_id) } guid { Fabricate.sequence(:guid) } + parent_guid { Fabricate.sequence(:guid) } parent_type "Post" end @@ -108,11 +108,6 @@ module DiasporaFederation created_at { Time.now.utc } end - Fabricator(:request_entity, class_name: DiasporaFederation::Entities::Request) do - author { Fabricate.sequence(:diaspora_id) } - recipient { Fabricate.sequence(:diaspora_id) } - end - Fabricator(:contact_entity, class_name: DiasporaFederation::Entities::Contact) do author { Fabricate.sequence(:diaspora_id) } recipient { Fabricate.sequence(:diaspora_id) } @@ -142,7 +137,7 @@ module DiasporaFederation participants { Array.new(3) { Fabricate.sequence(:diaspora_id) }.join(";") } end - Fabricator(:message_entity, class_name: DiasporaFederation::Entities::Message, from: :relayable_entity) do + Fabricator(:message_entity, class_name: DiasporaFederation::Entities::Message) do guid { Fabricate.sequence(:guid) } author { Fabricate.sequence(:diaspora_id) } text "this is a very informative text" @@ -150,13 +145,6 @@ module DiasporaFederation conversation_guid { Fabricate.sequence(:guid) } end - Fabricator(:relayable_retraction_entity, class_name: DiasporaFederation::Entities::RelayableRetraction) do - author { Fabricate.sequence(:diaspora_id) } - target_guid { Fabricate.sequence(:guid) } - target_type "Comment" - target {|attrs| Fabricate(:related_entity, author: attrs[:author]) } - end - Fabricator(:reshare_entity, class_name: DiasporaFederation::Entities::Reshare) do root_author { Fabricate.sequence(:diaspora_id) } root_guid { Fabricate.sequence(:guid) } @@ -174,13 +162,6 @@ module DiasporaFederation target {|attrs| Fabricate(:related_entity, author: attrs[:author]) } end - Fabricator(:signed_retraction_entity, class_name: DiasporaFederation::Entities::SignedRetraction) do - author { Fabricate.sequence(:diaspora_id) } - target_guid { Fabricate.sequence(:guid) } - target_type "Post" - target {|attrs| Fabricate(:related_entity, author: attrs[:author]) } - end - Fabricator(:poll_answer_entity, class_name: DiasporaFederation::Entities::PollAnswer) do guid { Fabricate.sequence(:guid) } answer { "Obama is a bicycle" } diff --git a/lib/diaspora_federation/validators.rb b/lib/diaspora_federation/validators.rb index 5b0d3ed..530b14b 100644 --- a/lib/diaspora_federation/validators.rb +++ b/lib/diaspora_federation/validators.rb @@ -62,8 +62,3 @@ require "diaspora_federation/validators/reshare_validator" require "diaspora_federation/validators/retraction_validator" require "diaspora_federation/validators/status_message_validator" require "diaspora_federation/validators/web_finger_validator" - -# deprecated -require "diaspora_federation/validators/relayable_retraction_validator" -require "diaspora_federation/validators/request_validator" -require "diaspora_federation/validators/signed_retraction_validator" diff --git a/lib/diaspora_federation/validators/relayable_retraction_validator.rb b/lib/diaspora_federation/validators/relayable_retraction_validator.rb deleted file mode 100644 index be5e4ad..0000000 --- a/lib/diaspora_federation/validators/relayable_retraction_validator.rb +++ /dev/null @@ -1,15 +0,0 @@ -module DiasporaFederation - module Validators - # This validates a {Entities::RelayableRetraction}. - # @deprecated The {Entities::RelayableRetraction} will be replaced with {Entities::Retraction}. - class RelayableRetractionValidator < Validation::Validator - include Validation - - rule :target_guid, :guid - rule :target_type, :not_empty - rule :target, :not_nil - - rule :author, %i(not_empty diaspora_id) - end - end -end diff --git a/lib/diaspora_federation/validators/request_validator.rb b/lib/diaspora_federation/validators/request_validator.rb deleted file mode 100644 index 9a10300..0000000 --- a/lib/diaspora_federation/validators/request_validator.rb +++ /dev/null @@ -1,12 +0,0 @@ -module DiasporaFederation - module Validators - # This validates a {Entities::Request}. - # @deprecated The {Entities::Request} will be replaced with {Entities::Contact}. - class RequestValidator < Validation::Validator - include Validation - - rule :author, %i(not_empty diaspora_id) - rule :recipient, %i(not_empty diaspora_id) - end - end -end diff --git a/lib/diaspora_federation/validators/signed_retraction_validator.rb b/lib/diaspora_federation/validators/signed_retraction_validator.rb deleted file mode 100644 index 414784f..0000000 --- a/lib/diaspora_federation/validators/signed_retraction_validator.rb +++ /dev/null @@ -1,15 +0,0 @@ -module DiasporaFederation - module Validators - # This validates a {Entities::SignedRetraction}. - # @deprecated The {Entities::RelayableRetraction} will be replaced with {Entities::Retraction}. - class SignedRetractionValidator < Validation::Validator - include Validation - - rule :target_guid, :guid - rule :target_type, :not_empty - rule :target, :not_nil - - rule :author, %i(not_empty diaspora_id) - end - end -end diff --git a/spec/controllers/diaspora_federation/fetch_controller_spec.rb b/spec/controllers/diaspora_federation/fetch_controller_spec.rb index 8ad8548..235783b 100644 --- a/spec/controllers/diaspora_federation/fetch_controller_spec.rb +++ b/spec/controllers/diaspora_federation/fetch_controller_spec.rb @@ -14,7 +14,7 @@ module DiasporaFederation expect_callback(:fetch_public_key, alice.diaspora_id).and_return(alice.public_key) - magic_env_xml = Nokogiri::XML::Document.parse(response.body).root + magic_env_xml = Nokogiri::XML(response.body).root magic_env = Salmon::MagicEnvelope.unenvelop(magic_env_xml) entity = magic_env.payload @@ -33,7 +33,7 @@ module DiasporaFederation expect_callback(:fetch_public_key, alice.diaspora_id).and_return(alice.public_key) - magic_env_xml = Nokogiri::XML::Document.parse(response.body).root + magic_env_xml = Nokogiri::XML(response.body).root magic_env = Salmon::MagicEnvelope.unenvelop(magic_env_xml) entity = magic_env.payload diff --git a/spec/entities.rb b/spec/entities.rb index e452fa9..8200935 100644 --- a/spec/entities.rb +++ b/spec/entities.rb @@ -58,17 +58,11 @@ module DiasporaFederation end class SomeRelayable < DiasporaFederation::Entity - LEGACY_SIGNATURE_ORDER = %i(guid author property parent_guid).freeze - PARENT_TYPE = "Parent".freeze include Entities::Relayable property :property, :string - - def parent_type - PARENT_TYPE - end end end diff --git a/spec/integration/comment_integration_spec.rb b/spec/integration/comment_integration_spec.rb index f7d5c6a..769f480 100644 --- a/spec/integration/comment_integration_spec.rb +++ b/spec/integration/comment_integration_spec.rb @@ -63,7 +63,7 @@ KEY ) } - let(:legacy_comment_xml_alice) { <<-XML } + let(:legacy_format_comment_xml_alice) { <<-XML } @@ -77,7 +77,7 @@ KEY XML - let(:new_signature_comment_xml_alice) { <<-XML } + let(:new_format_comment_xml_alice) { <<-XML } alice@pod-a.org e21589b0b41101333b870f77ba60fa73 @@ -99,7 +99,7 @@ XML XML - let(:legacy_comment_xml_bob) { <<-XML } + let(:legacy_format_comment_xml_bob) { <<-XML } @@ -113,7 +113,17 @@ XML XML - let(:legacy_new_signature_comment_xml_bob) { <<-XML } + let(:legacy_order_new_format_comment_xml_bob) { <<-XML } + + e21589b0b41101333b870f77ba60fa73 + 9e269ae0b41201333b8c0f77ba60fa73 + this is a very informative comment + alice@pod-a.org + XU5X1uqTh8SY6JMG9uhEVR5Rg7FURV6lpRwl/HYOu6DJ3Hd9tpA2aSFFibUxxsMgJXKNrrc5SykrrEdTiQoEei+j0QqZf3B5R7r84qgK7M46KazwIpqRPwVl2MdA/0DdQyYJLA/oavNj1nwll9vtR87M7e/C94qG6P+iQTMBQzo= + QqWSdwpb+/dcJUxuKKVe7aiz1NivXzlIdWZ71xyrxnhFxFYd+7EIittyTcp1cVehjg96pwDbn++P/rWyCffqenWu025DHvUfSmQkC93Z0dX6r3OIUlZqwEggtOdbunybiE++F3BVsGt5wC4YbAESB5ZFuhFVhBXh1X+EaZ/qoKo= + +XML + let(:new_order_legacy_format_comment_xml_bob) { <<-XML } @@ -127,7 +137,7 @@ XML XML - let(:new_signature_comment_xml_bob) { <<-XML } + let(:new_format_comment_xml_bob) { <<-XML } alice@pod-a.org e21589b0b41101333b870f77ba60fa73 @@ -137,7 +147,7 @@ XML hWsagsczmZD6d36d6MFdTt3hKAdnRtupSIU6464G2kkMJ+WlExxMgbF6kWR+jVCBTeKipWCYK3Arnj0YkuIZM9d14bJGVMTsW/ZzNfJ69bXZhsyawI8dPnZnLVydo+hU/XmGJBEuh2TOj9Emq6/HCYiWzPTF5qhYAtyJ1oxJ4Yk= XML - let(:legacy_new_data_comment_xml_bob) { <<-XML } + let(:legacy_format_new_data_comment_xml_bob) { <<-XML } @@ -178,7 +188,7 @@ XML 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::Document.parse(new_data_comment_xml_alice).root + xml = Nokogiri::XML(new_data_comment_xml_alice).root Salmon::XmlPayload.unpack(xml).to_xml end end @@ -190,22 +200,22 @@ XML expect_callback(:fetch_related_entity, "Post", parent_guid).and_return(parent) end - it "relays legacy signatures and xml" do - xml = Nokogiri::XML::Document.parse(legacy_comment_xml_alice).root + it "relays legacy order" do + xml = Nokogiri::XML(legacy_format_comment_xml_alice).root entity = Salmon::XmlPayload.unpack(xml) - expect(Salmon::XmlPayload.pack(entity).to_xml).to eq(legacy_comment_xml_bob.strip) + expect(entity.to_xml.to_xml).to eq(legacy_order_new_format_comment_xml_bob.strip) end - it "relays new signatures and xml" do - xml = Nokogiri::XML::Document.parse(new_signature_comment_xml_alice).root + it "relays new order" do + xml = Nokogiri::XML(new_format_comment_xml_alice).root entity = Salmon::XmlPayload.unpack(xml) - expect(Salmon::XmlPayload.pack(entity).to_xml).to eq(legacy_new_signature_comment_xml_bob.strip) + expect(entity.to_xml.to_xml).to eq(new_format_comment_xml_bob.strip) end - it "relays new signatures with new data" do - xml = Nokogiri::XML::Document.parse(new_data_comment_xml_alice).root + it "relays new data" do + xml = Nokogiri::XML(new_data_comment_xml_alice).root entity = Salmon::XmlPayload.unpack(xml) - expect(Salmon::XmlPayload.pack(entity).to_xml).to eq(legacy_new_data_comment_xml_bob.strip) + expect(entity.to_xml.to_xml).to eq(new_data_comment_xml_bob.strip) end end @@ -218,32 +228,40 @@ XML expect_callback(:fetch_related_entity, "Post", parent_guid).and_return(parent) end - it "parses legacy signatures and xml" do - xml = Nokogiri::XML::Document.parse(legacy_comment_xml_bob).root + 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 new signatures with legacy xml" do - xml = Nokogiri::XML::Document.parse(legacy_new_signature_comment_xml_bob).root + 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 signatures and xml" do - xml = Nokogiri::XML::Document.parse(new_signature_comment_xml_bob).root + 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) expect(entity.author).to eq(author) expect(entity.text).to eq(text) end - it "parses new data with legacy xml" do - xml = Nokogiri::XML::Document.parse(legacy_new_data_comment_xml_bob).root + it "parses new xml format" do + xml = Nokogiri::XML(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 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) @@ -251,8 +269,8 @@ XML expect(entity.additional_data["new_data"]).to eq(new_data) end - it "parses new xml with additional data" do - xml = Nokogiri::XML::Document.parse(new_data_comment_xml_bob).root + it "parses new data with new xml format" do + xml = Nokogiri::XML(new_data_comment_xml_bob).root entity = Salmon::XmlPayload.unpack(xml) expect(entity.author).to eq(author) diff --git a/spec/lib/diaspora_federation/entities/account_deletion_spec.rb b/spec/lib/diaspora_federation/entities/account_deletion_spec.rb index eaab619..1de2ecf 100644 --- a/spec/lib/diaspora_federation/entities/account_deletion_spec.rb +++ b/spec/lib/diaspora_federation/entities/account_deletion_spec.rb @@ -4,7 +4,7 @@ module DiasporaFederation let(:xml) { <<-XML } - #{data[:author]} + #{data[:author]} XML diff --git a/spec/lib/diaspora_federation/entities/account_migration_spec.rb b/spec/lib/diaspora_federation/entities/account_migration_spec.rb index 1029e48..6a5b8c2 100644 --- a/spec/lib/diaspora_federation/entities/account_migration_spec.rb +++ b/spec/lib/diaspora_federation/entities/account_migration_spec.rb @@ -19,7 +19,7 @@ module DiasporaFederation #{data[:author]} - #{data[:profile].author} + #{data[:profile].author} #{data[:profile].first_name} #{data[:profile].image_url} diff --git a/spec/lib/diaspora_federation/entities/comment_spec.rb b/spec/lib/diaspora_federation/entities/comment_spec.rb index f6b32d9..2ea3099 100644 --- a/spec/lib/diaspora_federation/entities/comment_spec.rb +++ b/spec/lib/diaspora_federation/entities/comment_spec.rb @@ -15,10 +15,10 @@ module DiasporaFederation let(:xml) { <<-XML } + #{data[:author]} #{data[:guid]} #{parent.guid} #{data[:text]} - #{data[:author]} #{data[:author_signature]} #{data[:parent_author_signature]} @@ -37,10 +37,10 @@ XML "created_at": "#{data[:created_at].iso8601}" }, "property_order": [ + "author", "guid", "parent_guid", - "text", - "author" + "text" ] } JSON @@ -59,7 +59,7 @@ JSON describe "#created_at" do it "has a created_at after parse" do - entity = described_class.from_xml(Nokogiri::XML::Document.parse(xml).root) + entity = described_class.from_xml(Nokogiri::XML(xml).root) expect(entity.created_at).to be_within(1).of(Time.now.utc) end diff --git a/spec/lib/diaspora_federation/entities/conversation_spec.rb b/spec/lib/diaspora_federation/entities/conversation_spec.rb index bc4ff5c..28349ab 100644 --- a/spec/lib/diaspora_federation/entities/conversation_spec.rb +++ b/spec/lib/diaspora_federation/entities/conversation_spec.rb @@ -29,11 +29,11 @@ module DiasporaFederation let(:xml) { <<-XML } - #{data[:author]} + #{data[:author]} #{parent.guid} #{data[:subject]} #{data[:created_at].utc.iso8601} - #{data[:participants]} + #{data[:participants]} #{data[:messages].map {|a| indent(a.to_xml.to_s, 2) }.join("\n")} XML @@ -56,7 +56,7 @@ XML XML - parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML::Document.parse(minimal_xml).root) + parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML(minimal_xml).root) expect(parsed_instance.messages).to eq([]) end end diff --git a/spec/lib/diaspora_federation/entities/event_spec.rb b/spec/lib/diaspora_federation/entities/event_spec.rb index 5a759e6..84afaa3 100644 --- a/spec/lib/diaspora_federation/entities/event_spec.rb +++ b/spec/lib/diaspora_federation/entities/event_spec.rb @@ -40,7 +40,7 @@ XML XML - parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML::Document.parse(minimal_xml).root) + parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML(minimal_xml).root) expect(parsed_instance.end).to be_nil expect(parsed_instance.all_day).to be_falsey expect(parsed_instance.timezone).to be_nil diff --git a/spec/lib/diaspora_federation/entities/like_spec.rb b/spec/lib/diaspora_federation/entities/like_spec.rb index 08fbc5b..ae90f5b 100644 --- a/spec/lib/diaspora_federation/entities/like_spec.rb +++ b/spec/lib/diaspora_federation/entities/like_spec.rb @@ -14,11 +14,11 @@ module DiasporaFederation let(:xml) { <<-XML } - #{data[:positive]} + #{data[:author]} #{data[:guid]} - #{parent.entity_type} #{parent.guid} - #{data[:author]} + #{parent.entity_type} + #{data[:positive]} #{data[:author_signature]} #{data[:parent_author_signature]} @@ -33,15 +33,15 @@ XML "parent_guid": "#{parent.guid}", "author_signature": "#{data[:author_signature]}", "parent_author_signature": "#{data[:parent_author_signature]}", - "positive": #{data[:positive]}, - "parent_type": "#{parent.entity_type}" + "parent_type": "#{parent.entity_type}", + "positive": #{data[:positive]} }, "property_order": [ - "positive", + "author", "guid", - "parent_type", "parent_guid", - "author" + "parent_type", + "positive" ] } JSON @@ -69,7 +69,7 @@ JSON XML expect { - DiasporaFederation::Entities::Like.from_xml(Nokogiri::XML::Document.parse(broken_xml).root) + DiasporaFederation::Entities::Like.from_xml(Nokogiri::XML(broken_xml).root) }.to raise_error Entity::ValidationError, "invalid DiasporaFederation::Entities::Like! missing 'parent_type'." end @@ -83,7 +83,7 @@ XML XML expect { - DiasporaFederation::Entities::Like.from_xml(Nokogiri::XML::Document.parse(broken_xml).root) + DiasporaFederation::Entities::Like.from_xml(Nokogiri::XML(broken_xml).root) }.to raise_error Entity::ValidationError, "invalid DiasporaFederation::Entities::Like! missing 'parent_guid'." end end diff --git a/spec/lib/diaspora_federation/entities/message_spec.rb b/spec/lib/diaspora_federation/entities/message_spec.rb index 67f9999..25412b0 100644 --- a/spec/lib/diaspora_federation/entities/message_spec.rb +++ b/spec/lib/diaspora_federation/entities/message_spec.rb @@ -1,108 +1,21 @@ module DiasporaFederation describe Entities::Message do - let(:parent) { Fabricate(:conversation, author: bob) } - let(:parent_entity) { Fabricate(:related_entity, author: bob.diaspora_id) } - let(:data) { - Fabricate - .attributes_for(:message_entity, author: alice.diaspora_id, parent_guid: parent.guid, parent: parent_entity) - .tap {|hash| add_signatures(hash) } - } + let(:data) { Fabricate.attributes_for(:message_entity, author: alice.diaspora_id) } let(:xml) { <<-XML } - - #{data[:guid]} - #{parent.guid} - #{data[:text]} - #{data[:created_at]} - #{data[:author]} - #{data[:conversation_guid]} - #{data[:author_signature]} - #{data[:parent_author_signature]} - -XML - - let(:string) { "Message:#{data[:guid]}:#{parent.guid}" } - - it_behaves_like "an Entity subclass" - - it_behaves_like "an XML Entity", %i(parent parent_guid) - - it_behaves_like "a relayable Entity" - - describe "#sender_valid?" do - let(:entity) { Entities::Message.new(data) } - - it "allows the author" do - expect(entity.sender_valid?(alice.diaspora_id)).to be_truthy - end - - it "allows parent author if the signature is valid" do - expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(parent_entity) - expect_callback(:fetch_public_key, alice.diaspora_id).and_return(alice.private_key) - expect(entity.sender_valid?(bob.diaspora_id)).to be_truthy - end - - it "does not allow any other person" do - expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(parent_entity) - invalid_sender = Fabricate.sequence(:diaspora_id) - expect(entity.sender_valid?(invalid_sender)).to be_falsey - end - - it "does not allow the parent author if the signature is invalid" do - expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(parent_entity) - expect_callback(:fetch_public_key, alice.diaspora_id).and_return(alice.private_key) - invalid_msg = Entities::Message.new(data.merge(author_signature: "aa")) - expect { - invalid_msg.sender_valid?(bob.diaspora_id) - }.to raise_error Entities::Relayable::SignatureVerificationFailed, "wrong author_signature for #{invalid_msg}" - end - - it "raises NotFetchable if the parent Conversation can not be found" do - expect_callback(:fetch_related_entity, "Conversation", entity.conversation_guid).and_return(nil) - expect { - entity.sender_valid?(bob.diaspora_id) - }.to raise_error Federation::Fetcher::NotFetchable - end - end - - context "relayable signature verification" do - it "does not verify the signature" do - data.merge!(author_signature: "aa", parent_author_signature: "bb") - xml = Entities::Message.new(data).to_xml - - expect { - Entities::Message.from_xml(xml) - }.not_to raise_error - end - end - - describe ".from_xml" do - it "adds a nil parent" do - xml = Entities::Message.new(data).to_xml - parsed = Entities::Message.from_xml(xml) - expect(parsed.parent).to be_nil - end - - it "uses the parent_guid from the parsed xml" do - xml = Entities::Message.new(data).to_xml - parsed = Entities::Message.from_xml(xml) - expect(parsed.parent_guid).to eq(data[:parent_guid]) - end - - it "uses nil for parent_guid if not in the xml" do - xml = <<-XML #{data[:author]} #{data[:guid]} #{data[:text]} - #{data[:created_at]} + #{data[:created_at].utc.iso8601} #{data[:conversation_guid]} XML - parsed = Entities::Message.from_xml(Nokogiri::XML::Document.parse(xml).root) - expect(parsed.parent_guid).to be_nil - end - end + let(:string) { "Message:#{data[:guid]}:#{data[:conversation_guid]}" } + + it_behaves_like "an Entity subclass" + + it_behaves_like "an XML Entity" end end diff --git a/spec/lib/diaspora_federation/entities/participation_spec.rb b/spec/lib/diaspora_federation/entities/participation_spec.rb index cc1276e..819e4a0 100644 --- a/spec/lib/diaspora_federation/entities/participation_spec.rb +++ b/spec/lib/diaspora_federation/entities/participation_spec.rb @@ -1,25 +1,21 @@ module DiasporaFederation describe Entities::Participation do let(:parent) { Fabricate(:post, author: bob) } - let(:parent_entity) { Fabricate(:related_entity, author: bob.diaspora_id) } let(:data) { Fabricate.attributes_for( :participation_entity, author: alice.diaspora_id, parent_guid: parent.guid, - parent_type: parent.entity_type, - parent: parent_entity - ).tap {|hash| add_signatures(hash) } + parent_type: parent.entity_type + ) } let(:xml) { <<-XML } + #{data[:author]} #{data[:guid]} - #{parent.entity_type} #{parent.guid} - #{data[:author]} - #{data[:author_signature]} - #{data[:parent_author_signature]} + #{parent.entity_type} XML @@ -30,16 +26,8 @@ XML "author": "#{data[:author]}", "guid": "#{data[:guid]}", "parent_guid": "#{parent.guid}", - "author_signature": "#{data[:author_signature]}", - "parent_author_signature": "#{data[:parent_author_signature]}", "parent_type": "#{parent.entity_type}" - }, - "property_order": [ - "guid", - "parent_type", - "parent_guid", - "author" - ] + } } JSON @@ -47,36 +35,11 @@ JSON it_behaves_like "an Entity subclass" - it_behaves_like "an XML Entity", [:parent] + it_behaves_like "an XML Entity" it_behaves_like "a JSON Entity" - it_behaves_like "a relayable Entity" - - it_behaves_like "a relayable JSON entity" - - describe "#sender_valid?" do - let(:entity) { Entities::Participation.new(data) } - - it "allows the author" do - expect(entity.sender_valid?(alice.diaspora_id)).to be_truthy - end - - it "does not allow the parent author" do - expect(entity.sender_valid?(bob.diaspora_id)).to be_falsey - end - end - context "parse xml" do - it "does not verify the signature" do - data.merge!(author_signature: "aa", parent_author_signature: "bb") - xml = Entities::Participation.new(data).to_xml - - expect { - Entities::Participation.from_xml(xml) - }.not_to raise_error - end - describe "#validate_parent" do let(:participation) { allow(DiasporaFederation.callbacks).to receive(:trigger).and_call_original diff --git a/spec/lib/diaspora_federation/entities/person_spec.rb b/spec/lib/diaspora_federation/entities/person_spec.rb index 3e7b83e..c784896 100644 --- a/spec/lib/diaspora_federation/entities/person_spec.rb +++ b/spec/lib/diaspora_federation/entities/person_spec.rb @@ -5,10 +5,10 @@ module DiasporaFederation let(:xml) { <<-XML } #{data[:guid]} - #{data[:author]} + #{data[:author]} #{data[:url]} - #{data[:profile].author} + #{data[:profile].author} #{data[:profile].first_name} #{data[:profile].image_url} diff --git a/spec/lib/diaspora_federation/entities/photo_spec.rb b/spec/lib/diaspora_federation/entities/photo_spec.rb index 9c58728..bb30d9e 100644 --- a/spec/lib/diaspora_federation/entities/photo_spec.rb +++ b/spec/lib/diaspora_federation/entities/photo_spec.rb @@ -5,7 +5,7 @@ module DiasporaFederation let(:xml) { <<-XML } #{data[:guid]} - #{data[:author]} + #{data[:author]} #{data[:public]} #{data[:created_at].utc.iso8601} #{data[:remote_photo_path]} @@ -58,7 +58,7 @@ JSON XML - parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML::Document.parse(minimal_xml).root) + parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML(minimal_xml).root) expect(parsed_instance.public).to be_falsey expect(parsed_instance.text).to be_nil end diff --git a/spec/lib/diaspora_federation/entities/poll_participation_spec.rb b/spec/lib/diaspora_federation/entities/poll_participation_spec.rb index b63f261..8d3c438 100644 --- a/spec/lib/diaspora_federation/entities/poll_participation_spec.rb +++ b/spec/lib/diaspora_federation/entities/poll_participation_spec.rb @@ -13,9 +13,9 @@ module DiasporaFederation let(:xml) { <<-XML } + #{data[:author]} #{data[:guid]} #{parent.guid} - #{data[:author]} #{data[:poll_answer_guid]} #{data[:author_signature]} #{data[:parent_author_signature]} @@ -34,9 +34,9 @@ XML "poll_answer_guid": "#{data[:poll_answer_guid]}" }, "property_order": [ + "author", "guid", "parent_guid", - "author", "poll_answer_guid" ] } diff --git a/spec/lib/diaspora_federation/entities/profile_spec.rb b/spec/lib/diaspora_federation/entities/profile_spec.rb index 7499e60..4994ba0 100644 --- a/spec/lib/diaspora_federation/entities/profile_spec.rb +++ b/spec/lib/diaspora_federation/entities/profile_spec.rb @@ -4,7 +4,7 @@ module DiasporaFederation let(:xml) { <<-XML } - #{data[:author]} + #{data[:author]} #{data[:first_name]} #{data[:image_url]} @@ -59,7 +59,7 @@ JSON XML - parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML::Document.parse(minimal_xml).root) + parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML(minimal_xml).root) expect(parsed_instance.first_name).to be_nil expect(parsed_instance.last_name).to be_nil expect(parsed_instance.image_url).to be_nil diff --git a/spec/lib/diaspora_federation/entities/relayable_retraction_spec.rb b/spec/lib/diaspora_federation/entities/relayable_retraction_spec.rb index 3f062c8..09bfce4 100644 --- a/spec/lib/diaspora_federation/entities/relayable_retraction_spec.rb +++ b/spec/lib/diaspora_federation/entities/relayable_retraction_spec.rb @@ -8,22 +8,11 @@ module DiasporaFederation parent: Fabricate(:related_entity, author: alice.diaspora_id) ) } - let(:data) { - Fabricate( - :relayable_retraction_entity, - author: alice.diaspora_id, - target_guid: target.guid, - target_type: target.entity_type, - target: target_entity - ).send(:enriched_properties).tap do |data| - data[:target_author_signature] = "" - data[:target] = target_entity - end - } + let(:data) { {author: alice.diaspora_id, target_guid: target.guid, target_type: target.entity_type} } let(:xml) { <<-XML } - #{data[:parent_author_signature]} + #{data[:target_guid]} #{data[:target_type]} #{data[:author]} @@ -31,94 +20,24 @@ module DiasporaFederation XML - let(:string) { "RelayableRetraction:#{data[:target_type]}:#{data[:target_guid]}" } - - it_behaves_like "an Entity subclass" - - it_behaves_like "an XML Entity", %i(parent_author_signature target_author_signature target) - - it_behaves_like "a retraction" - - describe "#to_xml" do - let(:author_pkey) { OpenSSL::PKey::RSA.generate(1024) } - let(:hash) { Fabricate.attributes_for(:relayable_retraction_entity) } - - it "updates author signature when it was nil and key was supplied and author is not parent author" do - parent = Fabricate(:related_entity, author: bob.diaspora_id) - hash[:target] = Fabricate(:related_entity, author: hash[:author], parent: parent) - - expect_callback(:fetch_private_key, hash[:author]).and_return(author_pkey) - - signed_string = "#{hash[:target_guid]};#{hash[:target_type]}" - - xml = Entities::RelayableRetraction.new(hash).to_xml - - signature = Base64.decode64(xml.at_xpath("target_author_signature").text) - expect(author_pkey.verify(OpenSSL::Digest::SHA256.new, signature, signed_string)).to be_truthy - end - - it "sets parent author signature when author is parent author" do - parent = Fabricate(:related_entity, author: hash[:author]) - hash[:target] = Fabricate(:related_entity, author: hash[:author], parent: parent) - - expect_callback(:fetch_private_key, hash[:author]).and_return(author_pkey) - - signed_string = "#{hash[:target_guid]};#{hash[:target_type]}" - - xml = Entities::RelayableRetraction.new(hash).to_xml - - signature = Base64.decode64(xml.at_xpath("parent_author_signature").text) - expect(author_pkey.verify(OpenSSL::Digest::SHA256.new, signature, signed_string)).to be_truthy - end - - it "updates parent author signature when it was nil, key was supplied and sender is author of the parent" do - parent = Fabricate(:related_entity, author: hash[:author]) - hash[:target] = Fabricate(:related_entity, author: bob.diaspora_id, parent: parent) - - expect_callback(:fetch_private_key, hash[:author]).and_return(author_pkey) - - signed_string = "#{hash[:target_guid]};#{hash[:target_type]}" - - xml = Entities::RelayableRetraction.new(hash).to_xml - - signature = Base64.decode64(xml.at_xpath("parent_author_signature").text) - expect(author_pkey.verify(OpenSSL::Digest::SHA256.new, signature, signed_string)).to be_truthy - end - - it "doesn't change signatures if they are already set" do - hash.merge!(target_author_signature: "aa", parent_author_signature: "bb") - - xml = Entities::RelayableRetraction.new(hash).to_xml - - expect(xml.at_xpath("target_author_signature").text).to eq("aa") - expect(xml.at_xpath("parent_author_signature").text).to eq("bb") - end - - it "doesn't change signatures if keys weren't supplied" do - expect_callback(:fetch_private_key, hash[:author]).and_return(nil) - - xml = Entities::RelayableRetraction.new(hash).to_xml - expect(xml.at_xpath("target_author_signature").text).to eq("") - expect(xml.at_xpath("parent_author_signature").text).to eq("") - end - end - - describe "#to_retraction" do - it "copies the attributes to a Retraction" do - relayable_retraction = Fabricate(:relayable_retraction_entity) - retraction = relayable_retraction.to_retraction - - expect(retraction).to be_a(Entities::Retraction) - expect(retraction.author).to eq(relayable_retraction.author) - expect(retraction.target_guid).to eq(relayable_retraction.target_guid) - expect(retraction.target_type).to eq(relayable_retraction.target_type) + describe "#initialize" do + it "raises because it is not supported anymore" do + expect { + Entities::RelayableRetraction.new(data) + }.to raise_error RuntimeError, + "Sending RelayableRetraction is not supported anymore! Use Retraction instead!" end end context "parse retraction" do it "parses the xml as a retraction" do - retraction = Entities::RelayableRetraction.from_xml(Nokogiri::XML::Document.parse(xml).root) + 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 diff --git a/spec/lib/diaspora_federation/entities/relayable_spec.rb b/spec/lib/diaspora_federation/entities/relayable_spec.rb index 0c02c4e..0d96e99 100644 --- a/spec/lib/diaspora_federation/entities/relayable_spec.rb +++ b/spec/lib/diaspora_federation/entities/relayable_spec.rb @@ -13,61 +13,88 @@ module DiasporaFederation 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(:legacy_signature_data) { "#{guid};#{author};#{property};#{parent_guid}" } + 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 - xml_order = [:author, :guid, :parent_guid, :property, "new_property", :author_signature] + signature_order = [:author, :guid, :parent_guid, :property, "new_property", :author_signature] - expect(Entities::SomeRelayable.new(hash, xml_order).xml_order) + expect(Entities::SomeRelayable.new(hash, signature_order).signature_order) .to eq([:author, :guid, :parent_guid, :property, "new_property"]) end end describe "#verify_signatures" do - it "doesn't raise anything if correct signatures with legacy-string were passed" do - hash[:author_signature] = sign_with_key(author_pkey, legacy_signature_data) - hash[:parent_author_signature] = sign_with_key(parent_pkey, legacy_signature_data) + it "doesn't raise anything if correct signatures were 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) expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key) - expect { Entities::SomeRelayable.new(hash).verify_signatures }.not_to raise_error + expect { Entities::SomeRelayable.new(hash, signature_order).verify_signatures }.not_to raise_error + end + + it "doesn't raise anything if correct signatures with new property were 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) + expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key) + + expect { + Entities::SomeRelayable.new(hash, signature_order, "new_property" => new_property).verify_signatures + }.not_to raise_error end it "raises when no public key for author was fetched" do expect_callback(:fetch_public_key, anything).and_return(nil) expect { - Entities::SomeRelayable.new(hash).verify_signatures + Entities::SomeRelayable.new(hash, signature_order).verify_signatures }.to raise_error Entities::Relayable::PublicKeyNotFound end - it "raises when bad author signature was passed" do + it "raises when no author signature was passed" do hash[:author_signature] = nil expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key) expect { - Entities::SomeRelayable.new(hash).verify_signatures + Entities::SomeRelayable.new(hash, signature_order).verify_signatures + }.to raise_error Entities::Relayable::SignatureVerificationFailed + end + + it "raises when bad author signature was passed" do + hash[:author_signature] = sign_with_key(author_pkey, "bad signed string") + + expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key) + + expect { + Entities::SomeRelayable.new(hash, signature_order).verify_signatures }.to raise_error Entities::Relayable::SignatureVerificationFailed end it "raises when no public key for parent author was fetched" do - hash[:author_signature] = sign_with_key(author_pkey, legacy_signature_data) + hash[:author_signature] = sign_with_key(author_pkey, signature_data) hash[:parent] = remote_parent expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key) expect_callback(:fetch_public_key, remote_parent.author).and_return(nil) expect { - Entities::SomeRelayable.new(hash).verify_signatures + Entities::SomeRelayable.new(hash, signature_order).verify_signatures }.to raise_error Entities::Relayable::PublicKeyNotFound end - it "raises when bad parent author signature was passed" do - hash[:author_signature] = sign_with_key(author_pkey, legacy_signature_data) + it "raises 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 @@ -75,68 +102,38 @@ module DiasporaFederation expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key) expect { - Entities::SomeRelayable.new(hash).verify_signatures + Entities::SomeRelayable.new(hash, signature_order).verify_signatures + }.to raise_error Entities::Relayable::SignatureVerificationFailed + end + + it "raises when bad parent author signature was passed" do + hash[:author_signature] = sign_with_key(author_pkey, signature_data) + hash[:parent_author_signature] = sign_with_key(parent_pkey, "bad signed string") + hash[:parent] = remote_parent + + expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key) + expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key) + + expect { + Entities::SomeRelayable.new(hash, signature_order).verify_signatures }.to raise_error Entities::Relayable::SignatureVerificationFailed end it "doesn't raise if parent_author_signature isn't set but we're on upstream federation" do - hash[:author_signature] = sign_with_key(author_pkey, legacy_signature_data) + 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).verify_signatures }.not_to raise_error - end - - context "new signatures" do - it "doesn't raise anything if correct signatures with new order were passed" do - xml_order = %i(author guid parent_guid property) - signature_data = "#{author};#{guid};#{parent_guid};#{property}" - - hash[:author_signature] = sign_with_key(author_pkey, signature_data) - hash[:parent_author_signature] = sign_with_key(parent_pkey, signature_data) - hash[:parent] = remote_parent - - expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key) - expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key) - - expect { Entities::SomeRelayable.new(hash, xml_order).verify_signatures }.not_to raise_error - end - - it "doesn't raise anything if correct signatures with new property were passed" do - xml_order = [:author, :guid, :parent_guid, :property, "new_property"] - signature_data_with_new_property = "#{author};#{guid};#{parent_guid};#{property};#{new_property}" - - hash[:author_signature] = sign_with_key(author_pkey, signature_data_with_new_property) - hash[:parent_author_signature] = sign_with_key(parent_pkey, signature_data_with_new_property) - hash[:parent] = remote_parent - - expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key) - expect_callback(:fetch_public_key, remote_parent.author).and_return(parent_pkey.public_key) - - expect { - Entities::SomeRelayable.new(hash, xml_order, "new_property" => new_property).verify_signatures - }.not_to raise_error - end - - it "raises with legacy-signatures and with new property and order" do - hash[:author_signature] = sign_with_key(author_pkey, legacy_signature_data) - - expect_callback(:fetch_public_key, author).and_return(author_pkey.public_key) - - xml_order = [:author, :guid, :parent_guid, :property, "new_property"] - expect { - Entities::SomeRelayable.new(hash, xml_order, "new_property" => new_property).verify_signatures - }.to raise_error Entities::Relayable::SignatureVerificationFailed - end + expect { Entities::SomeRelayable.new(hash, signature_order).verify_signatures }.not_to raise_error end end describe "#to_xml" do let(:expected_xml) { <<-XML } - #{author} + #{author} #{guid} #{parent_guid} #{property} @@ -147,23 +144,27 @@ module DiasporaFederation XML it "adds new unknown xml elements to the xml again" do - xml_order = [:author, :guid, :parent_guid, :property, "new_property"] - xml = Entities::SomeRelayable.new(hash_with_fake_signatures, xml_order, "new_property" => new_property).to_xml + signature_order = [:author, :guid, :parent_guid, :property, "new_property"] + xml = Entities::SomeRelayable.new( + hash_with_fake_signatures, signature_order, "new_property" => new_property + ).to_xml expect(xml.to_s.strip).to eq(expected_xml.strip) end - it "converts strings in xml_order to symbol if needed" do - xml_order = %w(author guid parent_guid property new_property) - xml = Entities::SomeRelayable.new(hash_with_fake_signatures, xml_order, "new_property" => new_property).to_xml + it "accepts string names of known properties in signature_order" do + signature_order = %w(author guid parent_guid property new_property) + xml = Entities::SomeRelayable.new( + hash_with_fake_signatures, signature_order, "new_property" => new_property + ).to_xml expect(xml.to_s.strip).to eq(expected_xml.strip) end - it "adds missing properties from xml_order to xml" do - xml_order = [:author, :guid, :parent_guid, :property, "new_property"] + it "adds missing properties from signature_order to xml" do + signature_order = [:author, :guid, :parent_guid, :property, "new_property"] - xml = Entities::SomeRelayable.new(hash_with_fake_signatures, xml_order).to_xml + xml = Entities::SomeRelayable.new(hash_with_fake_signatures, signature_order).to_xml expect(xml.at_xpath("new_property").text).to be_empty end @@ -177,18 +178,18 @@ 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, legacy_signature_data)).to be_truthy - expect(verify_signature(parent_pkey, parent_author_signature, legacy_signature_data)).to be_truthy + expect(verify_signature(author_pkey, author_signature, signature_data)).to be_truthy + expect(verify_signature(parent_pkey, parent_author_signature, signature_data)).to be_truthy end it "computes correct signatures 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) - xml_order = [:author, :guid, :parent_guid, "new_property", :property] + signature_order = [:author, :guid, :parent_guid, "new_property", :property] signature_data_with_new_property = "#{author};#{guid};#{parent_guid};#{new_property};#{property}" - xml = Entities::SomeRelayable.new(hash, xml_order, "new_property" => new_property).to_xml + xml = Entities::SomeRelayable.new(hash, signature_order, "new_property" => new_property).to_xml author_signature = xml.at_xpath("author_signature").text parent_author_signature = xml.at_xpath("parent_author_signature").text @@ -244,7 +245,7 @@ XML XML it "doesn't drop unknown properties" do - entity = Entities::SomeRelayable.from_xml(Nokogiri::XML::Document.parse(new_xml).root) + entity = Entities::SomeRelayable.from_xml(Nokogiri::XML(new_xml).root) expect(entity).to be_an_instance_of Entities::SomeRelayable expect(entity.property).to eq(property) @@ -254,14 +255,14 @@ XML end it "hand over the order in the xml to the instance without signatures" do - entity = Entities::SomeRelayable.from_xml(Nokogiri::XML::Document.parse(new_xml).root) + entity = Entities::SomeRelayable.from_xml(Nokogiri::XML(new_xml).root) - expect(entity.xml_order).to eq([:author, :guid, :parent_guid, "new_property", :property]) + expect(entity.signature_order).to eq([:author, :guid, :parent_guid, "new_property", :property]) end it "creates Entity with empty 'additional_data' if the xml has only known properties" do - hash[:author_signature] = sign_with_key(author_pkey, legacy_signature_data) - hash[:parent_author_signature] = sign_with_key(parent_pkey, legacy_signature_data) + hash[:author_signature] = sign_with_key(author_pkey, signature_data) + hash[:parent_author_signature] = sign_with_key(parent_pkey, signature_data) xml = Entities::SomeRelayable.new(hash).to_xml @@ -283,7 +284,7 @@ XML XML expect { - Entities::SomeRelayable.from_xml(Nokogiri::XML::Document.parse(broken_xml).root) + Entities::SomeRelayable.from_xml(Nokogiri::XML(broken_xml).root) }.to raise_error Entity::ValidationError, "invalid DiasporaFederation::Entities::SomeRelayable! missing 'parent_guid'." end @@ -300,11 +301,11 @@ XML expect(json).to include_json(property_order: property_order.map(&:to_s)) end - it "uses legacy order for filling property_order when no xml_order supplied" do + it "uses property order for filling property_order when no signature_order supplied" do entity = entity_class.new(hash_with_fake_signatures) expect( entity.to_json.to_json - ).to include_json(property_order: entity_class::LEGACY_SIGNATURE_ORDER.map(&:to_s)) + ).to include_json(property_order: %w(author guid parent_guid property)) end it "adds new unknown elements to the json again" do @@ -411,7 +412,7 @@ XML it "hands over the order in the data to the instance without signatures" do entity = Entities::SomeRelayable.from_hash(entity_data, property_order) - expect(entity.xml_order).to eq(%w(author guid parent_guid new_property property)) + expect(entity.signature_order).to eq([:author, :guid, :parent_guid, "new_property", :property]) end it "calls a constructor of the entity of the appropriate type" do @@ -433,15 +434,15 @@ XML end it "creates Entity with empty 'additional_data' if it has only known properties" do - property_order = %w(guid author property parent_guid) + 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, legacy_signature_data), - parent_author_signature: sign_with_key(parent_pkey, legacy_signature_data) + author_signature: sign_with_key(author_pkey, signature_data), + parent_author_signature: sign_with_key(parent_pkey, signature_data) } entity = Entities::SomeRelayable.from_hash(entity_data, property_order) diff --git a/spec/lib/diaspora_federation/entities/request_spec.rb b/spec/lib/diaspora_federation/entities/request_spec.rb index b59f7db..1826217 100644 --- a/spec/lib/diaspora_federation/entities/request_spec.rb +++ b/spec/lib/diaspora_federation/entities/request_spec.rb @@ -1,6 +1,6 @@ module DiasporaFederation describe Entities::Request do - let(:data) { Fabricate.attributes_for(:request_entity) } + let(:data) { {author: Fabricate.sequence(:diaspora_id), recipient: Fabricate.sequence(:diaspora_id)} } let(:xml) { <<-XML } @@ -9,29 +9,22 @@ module DiasporaFederation XML - let(:string) { "Request:#{data[:author]}:#{data[:recipient]}" } - - it_behaves_like "an Entity subclass" - - it_behaves_like "an XML Entity" - - describe "#to_contact" do - it "copies the attributes to a Contact" do - request = Fabricate(:request_entity) - contact = request.to_contact - - expect(contact).to be_a(Entities::Contact) - expect(contact.author).to eq(request.author) - expect(contact.recipient).to eq(request.recipient) - expect(contact.following).to be_truthy - expect(contact.sharing).to be_truthy + 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::Document.parse(xml).root) + 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 diff --git a/spec/lib/diaspora_federation/entities/reshare_spec.rb b/spec/lib/diaspora_federation/entities/reshare_spec.rb index bb680f7..e01ecf1 100644 --- a/spec/lib/diaspora_federation/entities/reshare_spec.rb +++ b/spec/lib/diaspora_federation/entities/reshare_spec.rb @@ -5,11 +5,11 @@ module DiasporaFederation let(:xml) { <<-XML } - #{data[:author]} + #{data[:author]} #{data[:guid]} #{data[:created_at].utc.iso8601} #{data[:provider_display_name]} - #{data[:root_author]} + #{data[:root_author]} #{data[:root_guid]} #{data[:public]} @@ -50,7 +50,7 @@ JSON XML - parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML::Document.parse(minimal_xml).root) + parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML(minimal_xml).root) expect(parsed_instance.public).to be_truthy expect(parsed_instance.provider_display_name).to be_nil end @@ -61,7 +61,7 @@ XML expect_callback(:fetch_related_entity, "Post", data[:root_guid]).and_return(nil) expect(Federation::Fetcher).to receive(:fetch_public).with(data[:root_author], "Post", data[:root_guid]) - Entities::Reshare.from_xml(Nokogiri::XML::Document.parse(xml).root) + Entities::Reshare.from_xml(Nokogiri::XML(xml).root) end end end diff --git a/spec/lib/diaspora_federation/entities/retraction_spec.rb b/spec/lib/diaspora_federation/entities/retraction_spec.rb index 5b3aa73..aad6821 100644 --- a/spec/lib/diaspora_federation/entities/retraction_spec.rb +++ b/spec/lib/diaspora_federation/entities/retraction_spec.rb @@ -13,9 +13,9 @@ module DiasporaFederation let(:xml) { <<-XML } - #{data[:author]} - #{data[:target_guid]} - #{data[:target_type]} + #{data[:author]} + #{data[:target_guid]} + #{data[:target_type]} XML @@ -25,7 +25,16 @@ XML it_behaves_like "an XML Entity" - it_behaves_like "a retraction" + context "receive with no target found" do + it "raises when no target is found" do + unknown_guid = Fabricate.sequence(:guid) + retraction = Entities::Retraction.new(data.merge(target_guid: unknown_guid)) + expect { + described_class.from_xml(retraction.to_xml) + }.to raise_error DiasporaFederation::Entities::Retraction::TargetNotFound, + "not found: #{data[:target_type]}:#{unknown_guid}" + end + end describe "#sender_valid?" do context "unrelayable target" do diff --git a/spec/lib/diaspora_federation/entities/signed_retraction_spec.rb b/spec/lib/diaspora_federation/entities/signed_retraction_spec.rb index 9daecef..0547299 100644 --- a/spec/lib/diaspora_federation/entities/signed_retraction_spec.rb +++ b/spec/lib/diaspora_federation/entities/signed_retraction_spec.rb @@ -2,80 +2,35 @@ module DiasporaFederation describe Entities::SignedRetraction do let(:target) { Fabricate(:post, author: alice) } let(:target_entity) { Fabricate(:related_entity, author: alice.diaspora_id) } - let(:data) { - Fabricate( - :signed_retraction_entity, - author: alice.diaspora_id, - target_guid: target.guid, - target_type: target.entity_type, - target: target_entity - ).send(:enriched_properties).merge(target: target_entity) - } + let(:data) { {author: alice.diaspora_id, target_guid: target.guid, target_type: target.entity_type} } let(:xml) { <<-XML } #{data[:target_guid]} #{data[:target_type]} #{data[:author]} - #{data[:target_author_signature]} + XML - let(:string) { "SignedRetraction:#{data[:target_type]}:#{data[:target_guid]}" } - - it_behaves_like "an Entity subclass" - - it_behaves_like "an XML Entity", [:target_author_signature] - - it_behaves_like "a retraction" - - describe "#to_xml" do - let(:author_pkey) { OpenSSL::PKey::RSA.generate(1024) } - let(:hash) { Fabricate.attributes_for(:signed_retraction_entity) } - - it "updates author signature when it was nil and key was supplied" do - expect_callback(:fetch_private_key, hash[:author]).and_return(author_pkey) - - signed_string = "#{hash[:target_guid]};#{hash[:target_type]}" - - xml = Entities::SignedRetraction.new(hash).to_xml - - signature = Base64.decode64(xml.at_xpath("target_author_signature").text) - expect(author_pkey.verify(OpenSSL::Digest::SHA256.new, signature, signed_string)).to be_truthy - end - - it "doesn't change signature if it is already set" do - hash[:target_author_signature] = "aa" - - xml = Entities::SignedRetraction.new(hash).to_xml - - expect(xml.at_xpath("target_author_signature").text).to eq("aa") - end - - it "doesn't change signature if a key wasn't supplied" do - expect_callback(:fetch_private_key, hash[:author]).and_return(nil) - - xml = Entities::SignedRetraction.new(hash).to_xml - expect(xml.at_xpath("target_author_signature").text).to eq("") - end - end - - describe "#to_retraction" do - it "copies the attributes to a Retraction" do - signed_retraction = Fabricate(:signed_retraction_entity) - retraction = signed_retraction.to_retraction - - expect(retraction).to be_a(Entities::Retraction) - expect(retraction.author).to eq(signed_retraction.author) - expect(retraction.target_guid).to eq(signed_retraction.target_guid) - expect(retraction.target_type).to eq(signed_retraction.target_type) + describe "#initialize" do + it "raises because it is not supported anymore" do + expect { + Entities::SignedRetraction.new(data) + }.to raise_error RuntimeError, + "Sending SignedRetraction is not supported anymore! Use Retraction instead!" end end context "parse retraction" do it "parses the xml as a retraction" do - retraction = Entities::SignedRetraction.from_xml(Nokogiri::XML::Document.parse(xml).root) + 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 diff --git a/spec/lib/diaspora_federation/entities/status_message_spec.rb b/spec/lib/diaspora_federation/entities/status_message_spec.rb index 1c1f8f4..1bdf01c 100644 --- a/spec/lib/diaspora_federation/entities/status_message_spec.rb +++ b/spec/lib/diaspora_federation/entities/status_message_spec.rb @@ -16,14 +16,14 @@ module DiasporaFederation let(:xml) { <<-XML } - #{data[:author]} + #{data[:author]} #{data[:guid]} #{data[:created_at].utc.iso8601} #{data[:provider_display_name]} - #{data[:text]} + #{data[:text]} #{photo1.guid} - #{photo1.author} + #{photo1.author} #{photo1.public} #{photo1.created_at.utc.iso8601} #{photo1.remote_photo_path} @@ -35,7 +35,7 @@ module DiasporaFederation #{photo2.guid} - #{photo2.author} + #{photo2.author} #{photo2.public} #{photo2.created_at.utc.iso8601} #{photo2.remote_photo_path} @@ -129,7 +129,7 @@ JSON XML - parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML::Document.parse(minimal_xml).root) + parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(Nokogiri::XML(minimal_xml).root) expect(parsed_instance.photos).to eq([]) expect(parsed_instance.location).to be_nil expect(parsed_instance.poll).to be_nil diff --git a/spec/lib/diaspora_federation/entity_spec.rb b/spec/lib/diaspora_federation/entity_spec.rb index 2bba34b..ec0df37 100644 --- a/spec/lib/diaspora_federation/entity_spec.rb +++ b/spec/lib/diaspora_federation/entity_spec.rb @@ -99,7 +99,7 @@ module DiasporaFederation it "replaces invalid XML characters" do entity = Entities::TestEntity.new(test: "asdfasdf asdf💩asdf\nasdf") xml = entity.to_xml.to_xml - parsed = Entities::TestEntity.from_xml(Nokogiri::XML::Document.parse(xml).root).test + parsed = Entities::TestEntity.from_xml(Nokogiri::XML(xml).root).test expect(parsed).to eq("asdf�asdf asdf💩asdf\nasdf") end end @@ -127,7 +127,7 @@ module DiasporaFederation test2: "qwer", test3: true ) - Entities::TestDefaultEntity.from_xml(Nokogiri::XML::Document.parse(<<-XML).root) + Entities::TestDefaultEntity.from_xml(Nokogiri::XML(<<-XML).root) asdf qwer @@ -140,11 +140,11 @@ XML arguments = [{arg1: "value"}] expect_any_instance_of(DiasporaFederation::Parsers::XmlParser).to receive(:parse).and_return(arguments) expect(Entities::TestDefaultEntity).to receive(:from_hash).with(*arguments) - Entities::TestDefaultEntity.from_xml(Nokogiri::XML::Document.parse("").root) + Entities::TestDefaultEntity.from_xml(Nokogiri::XML("").root) end it "passes input parameter directly to .parse method of the parser" do - root = Nokogiri::XML::Document.parse("").root + root = Nokogiri::XML("").root expect_any_instance_of(DiasporaFederation::Parsers::XmlParser).to receive(:parse).with(root) .and_return([{test1: "2", test2: "1"}]) Entities::TestDefaultEntity.from_xml(root) @@ -444,7 +444,7 @@ JSON XML - entity = Entities::TestNestedEntity.from_xml(Nokogiri::XML::Document.parse(xml).root) + entity = Entities::TestNestedEntity.from_xml(Nokogiri::XML(xml).root) expect(entity.asdf).to eq("FDSA") expect(entity.test).to be_nil @@ -455,15 +455,6 @@ XML context "xml_name" do let(:hash) { {test: "test", qwer: "qwer"} } - it "uses xml_name for the #to_xml" do - entity = Entities::TestEntityWithXmlName.new(hash) - xml_children = entity.to_xml.children - expect(xml_children).to have_exactly(2).items - xml_children.each do |node| - expect(%w(test asdf)).to include(node.name) - end - end - it "should not use the xml_name for the #to_h" do entity = Entities::TestEntityWithXmlName.new(hash) expect(entity.to_h).to eq(hash) diff --git a/spec/lib/diaspora_federation/federation/receiver_spec.rb b/spec/lib/diaspora_federation/federation/receiver_spec.rb index 0c924c8..5779a37 100644 --- a/spec/lib/diaspora_federation/federation/receiver_spec.rb +++ b/spec/lib/diaspora_federation/federation/receiver_spec.rb @@ -24,7 +24,7 @@ module DiasporaFederation it "parses the entity with legacy slap receiver" do expect_callback(:fetch_public_key, post.author).and_return(sender_key) - data = DiasporaFederation::Salmon::Slap.generate_xml(post.author, sender_key, post) + 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) @@ -65,8 +65,7 @@ module DiasporaFederation it "parses the entity with legacy slap receiver" do expect_callback(:fetch_public_key, post.author).and_return(sender_key) - data = DiasporaFederation::Salmon::EncryptedSlap.prepare(post.author, sender_key, post) - .generate_xml(recipient_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) @@ -88,7 +87,7 @@ module DiasporaFederation end it "redirects exceptions from the receiver" do - invalid_magic_env = Nokogiri::XML::Document.parse("").root + invalid_magic_env = Nokogiri::XML("").root data = Salmon::EncryptedMagicEnvelope.encrypt(invalid_magic_env, recipient_key.public_key) expect { diff --git a/spec/lib/diaspora_federation/federation/sender/hydra_wrapper_spec.rb b/spec/lib/diaspora_federation/federation/sender/hydra_wrapper_spec.rb index ed5203a..66f9698 100644 --- a/spec/lib/diaspora_federation/federation/sender/hydra_wrapper_spec.rb +++ b/spec/lib/diaspora_federation/federation/sender/hydra_wrapper_spec.rb @@ -3,6 +3,7 @@ module DiasporaFederation let(:sender_id) { Fabricate.sequence(:diaspora_id) } let(:obj_str) { "status_message@guid" } let(:xml) { "post" } + let(:json) { "{\"aes_key\": \"...\", \"encrypted_magic_envelope\": \"...\"}" } let(:url) { "http://example.org/receive/public" } let(:url2) { "http://example.com/receive/public" } @@ -13,21 +14,45 @@ module DiasporaFederation allow(Typhoeus::Hydra).to receive(:new).and_return(hydra) end - describe "#insert_job" do + describe "#insert_magic_env_request" do it "queues a request to hydra" do expect(hydra).to receive(:queue).with(kind_of(Typhoeus::Request)) expect(Typhoeus::Request).to receive(:new).with( - url, Federation::Sender::HydraWrapper.hydra_opts.merge(body: {xml: xml}) + url, + Federation::Sender::HydraWrapper.hydra_opts.merge( + body: xml, headers: Federation::Sender::HydraWrapper.xml_headers + ) ).and_call_original - hydra_wrapper.insert_job(url, xml) + hydra_wrapper.insert_magic_env_request(url, xml) end it "queues multiple requests to hydra" do expect(hydra).to receive(:queue).twice.with(kind_of(Typhoeus::Request)) - hydra_wrapper.insert_job(url, xml) - hydra_wrapper.insert_job(url2, xml) + hydra_wrapper.insert_magic_env_request(url, xml) + hydra_wrapper.insert_magic_env_request(url2, xml) + end + end + + describe "#insert_enc_magic_env_request" do + it "queues a request to hydra" do + expect(hydra).to receive(:queue).with(kind_of(Typhoeus::Request)) + expect(Typhoeus::Request).to receive(:new).with( + url, + Federation::Sender::HydraWrapper.hydra_opts.merge( + body: json, headers: Federation::Sender::HydraWrapper.json_headers + ) + ).and_call_original + + hydra_wrapper.insert_enc_magic_env_request(url, json) + end + + it "queues multiple requests to hydra" do + expect(hydra).to receive(:queue).twice.with(kind_of(Typhoeus::Request)) + + hydra_wrapper.insert_enc_magic_env_request(url, json) + hydra_wrapper.insert_enc_magic_env_request(url2, json) end end @@ -54,8 +79,8 @@ module DiasporaFederation before do Typhoeus.stub(url).and_return(response) Typhoeus.stub(url2).and_return(error_response) - hydra_wrapper.insert_job(url, xml) - hydra_wrapper.insert_job(url2, xml) + hydra_wrapper.insert_magic_env_request(url, xml) + hydra_wrapper.insert_magic_env_request(url2, xml) end before :all do WebMock::HttpLibAdapters::TyphoeusAdapter.disable! @@ -88,7 +113,7 @@ module DiasporaFederation return_code: :ok ) Typhoeus.stub("http://example.net/receive/not_found").and_return(not_found) - hydra_wrapper.insert_job("http://example.net/receive/not_found", xml) + hydra_wrapper.insert_magic_env_request("http://example.net/receive/not_found", xml) hydra_wrapper.send end @@ -100,7 +125,7 @@ module DiasporaFederation url3 = "http://example.net/receive/public" Typhoeus.stub(url3).and_return(response) - hydra_wrapper.insert_job(url3, xml) + hydra_wrapper.insert_magic_env_request(url3, xml) expect(hydra_wrapper.send).to eq([url2, url3]) end diff --git a/spec/lib/diaspora_federation/federation/sender_spec.rb b/spec/lib/diaspora_federation/federation/sender_spec.rb index 29a6a61..8f1dce9 100644 --- a/spec/lib/diaspora_federation/federation/sender_spec.rb +++ b/spec/lib/diaspora_federation/federation/sender_spec.rb @@ -13,8 +13,8 @@ module DiasporaFederation let(:urls) { ["https://example.org/receive/public", "https://example.com/receive/public"] } before do - expect(hydra_wrapper).to receive(:insert_job).with(urls.at(0), xml) - expect(hydra_wrapper).to receive(:insert_job).with(urls.at(1), xml) + expect(hydra_wrapper).to receive(:insert_magic_env_request).with(urls.at(0), xml) + expect(hydra_wrapper).to receive(:insert_magic_env_request).with(urls.at(1), xml) end it "returns empty array if send was successful" do @@ -34,14 +34,14 @@ module DiasporaFederation describe ".private" do let(:targets) { { - "https://example.org/receive/user/guid" => "post", - "https://example.com/receive/user/guid" => "post2" + "https://example.org/receive/user/guid" => "{\"aes_key\": \"key1\", \"encrypted_magic_envelope\": \"...\"}", + "https://example.com/receive/user/guid" => "{\"aes_key\": \"key2\", \"encrypted_magic_envelope\": \"...\"}" } } before do - targets.each do |url, xml| - expect(hydra_wrapper).to receive(:insert_job).with(url, xml) + targets.each do |url, json| + expect(hydra_wrapper).to receive(:insert_enc_magic_env_request).with(url, json) end end @@ -55,7 +55,9 @@ module DiasporaFederation expect(hydra_wrapper).to receive(:send).and_return(["https://example.com/receive/user/guid"]) expect(Federation::Sender.private(sender_id, obj_str, targets)) - .to eq("https://example.com/receive/user/guid" => "post2") + .to eq( + "https://example.com/receive/user/guid" => "{\"aes_key\": \"key2\", \"encrypted_magic_envelope\": \"...\"}" + ) end end end diff --git a/spec/lib/diaspora_federation/parsers/relayable_xml_parser_spec.rb b/spec/lib/diaspora_federation/parsers/relayable_xml_parser_spec.rb index e40b979..d5ef872 100644 --- a/spec/lib/diaspora_federation/parsers/relayable_xml_parser_spec.rb +++ b/spec/lib/diaspora_federation/parsers/relayable_xml_parser_spec.rb @@ -4,7 +4,7 @@ module DiasporaFederation let(:entity_class) { Entities::SomeRelayable } let(:xml_parser) { Parsers::RelayableXmlParser.new(entity_class) } it "passes order of the XML elements as a second argument in the returned list" do - xml_object = Nokogiri::XML::Document.parse(<<-XML).root + xml_object = Nokogiri::XML(<<-XML).root im a guid value diff --git a/spec/lib/diaspora_federation/parsers/xml_parser_spec.rb b/spec/lib/diaspora_federation/parsers/xml_parser_spec.rb index 010b35c..6f51387 100644 --- a/spec/lib/diaspora_federation/parsers/xml_parser_spec.rb +++ b/spec/lib/diaspora_federation/parsers/xml_parser_spec.rb @@ -18,7 +18,7 @@ module DiasporaFederation XML expect { - xml_parser.parse(Nokogiri::XML::Document.parse(xml).root) + xml_parser.parse(Nokogiri::XML(xml).root) }.to raise_error Parsers::BaseParser::InvalidRootNode, "'unknown_entity' can't be parsed by DiasporaFederation::Entities::TestComplexEntity" end @@ -39,7 +39,7 @@ XML XML - parsed = Parsers::XmlParser.new(Entities::TestEntityWithXmlName).parse(Nokogiri::XML::Document.parse(xml).root) + 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") @@ -53,7 +53,7 @@ XML XML - parsed = Parsers::XmlParser.new(Entities::TestEntityWithXmlName).parse(Nokogiri::XML::Document.parse(xml).root) + 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") @@ -68,7 +68,7 @@ XML XML - parsed = Parsers::XmlParser.new(Entities::TestDefaultEntity).parse(Nokogiri::XML::Document.parse(xml).root) + parsed = Parsers::XmlParser.new(Entities::TestDefaultEntity).parse(Nokogiri::XML(xml).root) expect(parsed[0][:test1]).to eq("asdf") expect(parsed[0][:test2]).to eq("qwer") @@ -82,7 +82,7 @@ XML XML - parsed = Parsers::XmlParser.new(Entities::TestEntityWithBoolean).parse(Nokogiri::XML::Document.parse(xml).root) + parsed = Parsers::XmlParser.new(Entities::TestEntityWithBoolean).parse(Nokogiri::XML(xml).root) expect(parsed[0][:test]).to eq(false) end @@ -95,7 +95,7 @@ XML XML parsed = Parsers::XmlParser.new(Entities::TestEntityWithBoolean).parse( - Nokogiri::XML::Document.parse(xml).root + Nokogiri::XML(xml).root ) expect(parsed[0][:test]).to be_nil end @@ -110,7 +110,7 @@ XML XML parsed = Parsers::XmlParser.new(Entities::TestEntityWithInteger).parse( - Nokogiri::XML::Document.parse(xml).root + Nokogiri::XML(xml).root ) expect(parsed[0][:test]).to be_nil end @@ -125,7 +125,7 @@ XML XML parsed = Parsers::XmlParser.new(Entities::TestEntityWithTimestamp).parse( - Nokogiri::XML::Document.parse(xml).root + Nokogiri::XML(xml).root ) expect(parsed[0][:test]).to be_nil end @@ -161,7 +161,7 @@ XML XML - parsed = Parsers::XmlParser.new(Entities::TestDefaultEntity).parse(Nokogiri::XML::Document.parse(xml).root) + parsed = Parsers::XmlParser.new(Entities::TestDefaultEntity).parse(Nokogiri::XML(xml).root) expect(parsed[0]["test_new"]).to eq("new_value") end end diff --git a/spec/lib/diaspora_federation/salmon/encrypted_magic_envelope_spec.rb b/spec/lib/diaspora_federation/salmon/encrypted_magic_envelope_spec.rb index 2c5bafd..7b5f264 100644 --- a/spec/lib/diaspora_federation/salmon/encrypted_magic_envelope_spec.rb +++ b/spec/lib/diaspora_federation/salmon/encrypted_magic_envelope_spec.rb @@ -32,7 +32,7 @@ module DiasporaFederation xml = Salmon::AES.decrypt(json["encrypted_magic_envelope"], key["key"], key["iv"]) - expect(Nokogiri::XML::Document.parse(xml).root.to_xml).to eq(magic_env.to_xml) + expect(Nokogiri::XML(xml).to_xml).to eq(magic_env.to_xml) end end diff --git a/spec/lib/diaspora_federation/salmon/encrypted_slap_spec.rb b/spec/lib/diaspora_federation/salmon/encrypted_slap_spec.rb index ac6d0d8..43349f0 100644 --- a/spec/lib/diaspora_federation/salmon/encrypted_slap_spec.rb +++ b/spec/lib/diaspora_federation/salmon/encrypted_slap_spec.rb @@ -4,137 +4,7 @@ module DiasporaFederation 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) { Salmon::EncryptedSlap.prepare(sender, privkey, payload).generate_xml(recipient_key.public_key) } - - context "generate" do - describe ".prepare" do - context "sanity" do - it "raises an error when the sender is the wrong type" do - [1234, true, :symbol, payload, privkey].each do |val| - expect { - Salmon::EncryptedSlap.prepare(val, privkey, payload) - }.to raise_error ArgumentError - end - end - - it "raises an error when the privkey is the wrong type" do - ["asdf", 1234, true, :symbol, payload].each do |val| - expect { - Salmon::EncryptedSlap.prepare(sender, val, payload) - }.to raise_error ArgumentError - end - end - - it "raises an error when the entity is the wrong type" do - ["asdf", 1234, true, :symbol, privkey].each do |val| - expect { - Salmon::EncryptedSlap.prepare(sender, privkey, val) - }.to raise_error ArgumentError - end - end - end - end - - describe ".generate_xml" do - let(:ns) { {d: Salmon::XMLNS, me: Salmon::MagicEnvelope::XMLNS} } - - context "sanity" do - it "accepts correct params" do - expect { - Salmon::EncryptedSlap.prepare(sender, privkey, payload).generate_xml(recipient_key.public_key) - }.not_to raise_error - end - - it "raises an error when the params are the wrong type" do - ["asdf", 1234, true, :symbol, payload].each do |val| - expect { - Salmon::EncryptedSlap.prepare(sender, privkey, payload).generate_xml(val) - }.to raise_error ArgumentError - end - end - end - - it "generates valid xml" do - doc = Nokogiri::XML::Document.parse(slap_xml) - expect(doc.root.name).to eq("diaspora") - expect(doc.at_xpath("d:diaspora/d:encrypted_header", ns).content).to_not be_empty - expect(doc.xpath("d:diaspora/me:env", ns)).to have(1).item - end - - it "can generate xml for two people" do - slap = Salmon::EncryptedSlap.prepare(sender, privkey, payload) - - doc1 = Nokogiri::XML::Document.parse(slap.generate_xml(recipient_key.public_key)) - enc_header1 = doc1.at_xpath("d:diaspora/d:encrypted_header", ns).content - cipher_header1 = JSON.parse(Base64.decode64(enc_header1)) - header_key1 = JSON.parse(recipient_key.private_decrypt(Base64.decode64(cipher_header1["aes_key"]))) - decrypted_header1 = Salmon::AES.decrypt(cipher_header1["ciphertext"], - Base64.decode64(header_key1["key"]), - Base64.decode64(header_key1["iv"])) - - recipient2_key = OpenSSL::PKey::RSA.generate(1024) - doc2 = Nokogiri::XML::Document.parse(slap.generate_xml(recipient2_key.public_key)) - enc_header2 = doc2.at_xpath("d:diaspora/d:encrypted_header", ns).content - cipher_header2 = JSON.parse(Base64.decode64(enc_header2)) - header_key2 = JSON.parse(recipient2_key.private_decrypt(Base64.decode64(cipher_header2["aes_key"]))) - decrypted_header2 = Salmon::AES.decrypt(cipher_header2["ciphertext"], - Base64.decode64(header_key2["key"]), - Base64.decode64(header_key2["iv"])) - - expect(enc_header1).not_to eq(enc_header2) - expect(header_key1).not_to eq(header_key2) - expect(decrypted_header1).to eq(decrypted_header2) - expect(doc1.xpath("d:diaspora/me:env", ns).to_xml).to eq(doc2.xpath("d:diaspora/me:env", ns).to_xml) - end - - it "does not add the sender to the magic envelope" do - doc = Nokogiri::XML::Document.parse(slap_xml) - expect(doc.at_xpath("d:diaspora/me:env/me:sig", ns)["key_id"]).to be_nil - end - - context "header" do - subject { - doc = Nokogiri::XML::Document.parse(slap_xml) - doc.at_xpath("d:diaspora/d:encrypted_header", ns).content - } - let(:cipher_header) { JSON.parse(Base64.decode64(subject)) } - let(:header_key) { - JSON.parse(recipient_key.private_decrypt(Base64.decode64(cipher_header["aes_key"]))) - } - - it "encodes the header correctly" do - json_header = {} - expect { - json_header = JSON.parse(Base64.decode64(subject)) - }.not_to raise_error - expect(json_header).to include("aes_key", "ciphertext") - end - - it "encrypts the public_key encrypted header correctly" do - key = {} - expect { - key = JSON.parse(recipient_key.private_decrypt(Base64.decode64(cipher_header["aes_key"]))) - }.not_to raise_error - expect(key).to include("key", "iv") - end - - it "encrypts the aes encrypted header correctly" do - header = "" - expect { - header = Salmon::AES.decrypt(cipher_header["ciphertext"], - Base64.decode64(header_key["key"]), - Base64.decode64(header_key["iv"])) - }.not_to raise_error - header_doc = Nokogiri::XML::Document.parse(header) - expect(header_doc.root.name).to eq("decrypted_header") - expect(header_doc.xpath("//iv")).to have(1).item - expect(header_doc.xpath("//aes_key")).to have(1).item - expect(header_doc.xpath("//author_id")).to have(1).item - expect(header_doc.at_xpath("//author_id").content).to eq(sender) - end - end - end - end + let(:slap_xml) { generate_legacy_encrypted_salmon_slap(payload, sender, privkey, recipient_key.public_key) } describe ".from_xml" do context "sanity" do diff --git a/spec/lib/diaspora_federation/salmon/magic_envelope_spec.rb b/spec/lib/diaspora_federation/salmon/magic_envelope_spec.rb index 47a5955..bc705da 100644 --- a/spec/lib/diaspora_federation/salmon/magic_envelope_spec.rb +++ b/spec/lib/diaspora_federation/salmon/magic_envelope_spec.rb @@ -6,10 +6,10 @@ module DiasporaFederation let(:envelope) { Salmon::MagicEnvelope.new(payload, sender) } def sig_subj(env) - data = Base64.urlsafe_decode64(env.at_xpath("me:data").content) - type = env.at_xpath("me:data")["type"] - enc = env.at_xpath("me:encoding").content - alg = env.at_xpath("me:alg").content + data = Base64.urlsafe_decode64(env.at_xpath("me:env/me:data").content) + type = env.at_xpath("me:env/me:data")["type"] + enc = env.at_xpath("me:env/me:encoding").content + alg = env.at_xpath("me:env/me:alg").content [data, type, enc, alg].map {|i| Base64.urlsafe_encode64(i) }.join(".") end @@ -41,12 +41,12 @@ module DiasporaFederation end end - it "should be an instance of Nokogiri::XML::Element" do - expect(envelope.envelop(privkey)).to be_an_instance_of Nokogiri::XML::Element + it "should be an instance of Nokogiri::XML::Document" do + expect(envelope.envelop(privkey)).to be_an_instance_of Nokogiri::XML::Document end it "returns a magic envelope of correct structure" do - env_xml = envelope.envelop(privkey) + env_xml = envelope.envelop(privkey).root expect(env_xml.name).to eq("env") control = %w(data encoding alg sig) @@ -59,13 +59,13 @@ module DiasporaFederation end it "adds the sender to the signature" do - key_id = envelope.envelop(privkey).at_xpath("me:sig")["key_id"] + key_id = envelope.envelop(privkey).at_xpath("me:env/me:sig")["key_id"] expect(Base64.urlsafe_decode64(key_id)).to eq(sender) end it "adds the data_type" do - data_type = envelope.envelop(privkey).at_xpath("me:data")["type"] + data_type = envelope.envelop(privkey).at_xpath("me:env/me:data")["type"] expect(data_type).to eq("application/xml") end @@ -74,35 +74,15 @@ module DiasporaFederation env_xml = envelope.envelop(privkey) subj = sig_subj(env_xml) - sig = Base64.urlsafe_decode64(env_xml.at_xpath("me:sig").content) + sig = Base64.urlsafe_decode64(env_xml.at_xpath("me:env/me:sig").content) expect(privkey.public_key.verify(OpenSSL::Digest::SHA256.new, sig, subj)).to be_truthy end end - describe "#encrypt!" do - it "encrypts the payload, returning cipher params" do - params = envelope.encrypt! - expect(params).to include(:key, :iv) - end - - it "actually encrypts the payload" do - plain_payload = envelope.send(:payload_data) - params = envelope.encrypt! - encrypted_payload = envelope.send(:payload_data) - - cipher = OpenSSL::Cipher.new(Salmon::AES::CIPHER) - cipher.encrypt - cipher.iv = params[:iv] - cipher.key = params[:key] - - ciphertext = cipher.update(plain_payload) + cipher.final - - expect(Base64.strict_encode64(ciphertext)).to eq(encrypted_payload) - end - end - describe ".unenvelop" do + let(:envelope_root) { envelope.envelop(privkey).root } + context "sanity" do before do allow(DiasporaFederation.callbacks).to receive(:trigger).with( @@ -112,7 +92,7 @@ module DiasporaFederation it "works with sane input" do expect { - Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey), sender) + Salmon::MagicEnvelope.unenvelop(envelope_root, sender) }.not_to raise_error end @@ -126,15 +106,14 @@ module DiasporaFederation it "verifies the envelope structure" do expect { - Salmon::MagicEnvelope.unenvelop(Nokogiri::XML::Document.parse("").root, sender) + Salmon::MagicEnvelope.unenvelop(Nokogiri::XML("").root, sender) }.to raise_error Salmon::InvalidEnvelope end it "raises if missing signature" do - bad_env = envelope.envelop(privkey) - bad_env.at_xpath("me:sig").remove + envelope_root.at_xpath("me:sig").remove expect { - Salmon::MagicEnvelope.unenvelop(bad_env, sender) + Salmon::MagicEnvelope.unenvelop(envelope_root, sender) }.to raise_error Salmon::InvalidEnvelope, "missing me:sig" end @@ -145,70 +124,63 @@ module DiasporaFederation expect_callback(:fetch_public_key, other_sender).and_return(other_key) expect { - Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey), other_sender) + Salmon::MagicEnvelope.unenvelop(envelope_root, other_sender) }.to raise_error Salmon::InvalidSignature end it "raises if missing data" do - bad_env = envelope.envelop(privkey) - bad_env.at_xpath("me:data").remove + envelope_root.at_xpath("me:data").remove expect { - Salmon::MagicEnvelope.unenvelop(bad_env, sender) + Salmon::MagicEnvelope.unenvelop(envelope_root, sender) }.to raise_error Salmon::InvalidEnvelope, "missing me:data" end it "raises if missing encoding" do - bad_env = envelope.envelop(privkey) - bad_env.at_xpath("me:encoding").remove + envelope_root.at_xpath("me:encoding").remove expect { - Salmon::MagicEnvelope.unenvelop(bad_env, sender) + Salmon::MagicEnvelope.unenvelop(envelope_root, sender) }.to raise_error Salmon::InvalidEncoding, "missing encoding" end it "verifies the encoding" do - bad_env = envelope.envelop(privkey) - bad_env.at_xpath("me:encoding").content = "invalid_enc" + envelope_root.at_xpath("me:encoding").content = "invalid_enc" expect { - Salmon::MagicEnvelope.unenvelop(bad_env, sender) + Salmon::MagicEnvelope.unenvelop(envelope_root, sender) }.to raise_error Salmon::InvalidEncoding, "invalid encoding: invalid_enc" end it "raises if missing algorithm" do - bad_env = envelope.envelop(privkey) - bad_env.at_xpath("me:alg").remove + envelope_root.at_xpath("me:alg").remove expect { - Salmon::MagicEnvelope.unenvelop(bad_env, sender) + Salmon::MagicEnvelope.unenvelop(envelope_root, sender) }.to raise_error Salmon::InvalidAlgorithm, "missing algorithm" end it "verifies the algorithm" do - bad_env = envelope.envelop(privkey) - bad_env.at_xpath("me:alg").content = "invalid_alg" + envelope_root.at_xpath("me:alg").content = "invalid_alg" expect { - Salmon::MagicEnvelope.unenvelop(bad_env, sender) + Salmon::MagicEnvelope.unenvelop(envelope_root, sender) }.to raise_error Salmon::InvalidAlgorithm, "invalid algorithm: invalid_alg" end it "raises if missing data type" do - bad_env = envelope.envelop(privkey) - bad_env.at_xpath("me:data").attributes["type"].remove + envelope_root.at_xpath("me:data").attributes["type"].remove expect { - Salmon::MagicEnvelope.unenvelop(bad_env, sender) + Salmon::MagicEnvelope.unenvelop(envelope_root, sender) }.to raise_error Salmon::InvalidDataType, "missing data type" end it "verifies the data type" do - bad_env = envelope.envelop(privkey) - bad_env.at_xpath("me:data")["type"] = "invalid_type" + envelope_root.at_xpath("me:data")["type"] = "invalid_type" expect { - Salmon::MagicEnvelope.unenvelop(bad_env, sender) + Salmon::MagicEnvelope.unenvelop(envelope_root, sender) }.to raise_error Salmon::InvalidDataType, "invalid data type: invalid_type" end end context "generated instance" do it_behaves_like "a MagicEnvelope instance" do - subject { Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey), sender) } + subject { Salmon::MagicEnvelope.unenvelop(envelope_root, sender) } end end @@ -216,8 +188,8 @@ module DiasporaFederation expect_callback(:fetch_public_key, sender).and_return(privkey.public_key) env = Salmon::MagicEnvelope.new(payload) - params = env.encrypt! - env_xml = env.envelop(privkey) + params = encrypt_magic_env(env) + env_xml = env.envelop(privkey).root magic_env = Salmon::MagicEnvelope.unenvelop(env_xml, sender, params) expect(magic_env.payload).to be_an_instance_of Entities::TestEntity @@ -227,17 +199,15 @@ module DiasporaFederation context "use key_id from magic envelope" do context "generated instance" do it_behaves_like "a MagicEnvelope instance" do - subject { Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey)) } + subject { Salmon::MagicEnvelope.unenvelop(envelope_root) } end end it "raises if the magic envelope has no key_id" do - bad_env = envelope.envelop(privkey) - - bad_env.at_xpath("me:sig").attributes["key_id"].remove + envelope_root.at_xpath("me:sig").attributes["key_id"].remove expect { - Salmon::MagicEnvelope.unenvelop(bad_env) + Salmon::MagicEnvelope.unenvelop(envelope_root) }.to raise_error Salmon::InvalidEnvelope end @@ -245,7 +215,7 @@ module DiasporaFederation expect_callback(:fetch_public_key, sender).and_return(nil) expect { - Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey)) + Salmon::MagicEnvelope.unenvelop(envelope_root) }.to raise_error Salmon::SenderKeyNotFound end end diff --git a/spec/lib/diaspora_federation/salmon/slap_spec.rb b/spec/lib/diaspora_federation/salmon/slap_spec.rb index 5143e70..465f827 100644 --- a/spec/lib/diaspora_federation/salmon/slap_spec.rb +++ b/spec/lib/diaspora_federation/salmon/slap_spec.rb @@ -3,49 +3,7 @@ module DiasporaFederation 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) { Salmon::Slap.generate_xml(sender, privkey, payload) } - - describe ".generate_xml" do - context "sanity" do - it "accepts correct params" do - expect { - Salmon::Slap.generate_xml(sender, privkey, payload) - }.not_to raise_error - end - - it "raises an error when the sender is the wrong type" do - [1234, true, :symbol, payload, privkey].each do |val| - expect { - Salmon::Slap.generate_xml(val, privkey, payload) - }.to raise_error ArgumentError - end - end - - it "raises an error when the privkey is the wrong type" do - ["asdf", 1234, true, :symbol, payload].each do |val| - expect { - Salmon::Slap.generate_xml(sender, val, payload) - }.to raise_error ArgumentError - end - end - - it "raises an error when the payload is the wrong type" do - ["asdf", 1234, true, :symbol, privkey].each do |val| - expect { - Salmon::Slap.generate_xml(sender, privkey, val) - }.to raise_error ArgumentError - end - end - end - - it "generates valid xml" do - ns = {d: Salmon::XMLNS, me: Salmon::MagicEnvelope::XMLNS} - doc = Nokogiri::XML::Document.parse(slap_xml) - expect(doc.root.name).to eq("diaspora") - expect(doc.at_xpath("d:diaspora/d:header/d:author_id", ns).content).to eq(sender) - expect(doc.xpath("d:diaspora/me:env", ns)).to have(1).item - end - end + let(:slap_xml) { generate_legacy_salmon_slap(payload, sender, privkey) } describe ".from_xml" do context "sanity" do diff --git a/spec/lib/diaspora_federation/salmon/xml_payload_spec.rb b/spec/lib/diaspora_federation/salmon/xml_payload_spec.rb index 1a4c2a9..c254580 100644 --- a/spec/lib/diaspora_federation/salmon/xml_payload_spec.rb +++ b/spec/lib/diaspora_federation/salmon/xml_payload_spec.rb @@ -1,7 +1,6 @@ module DiasporaFederation describe Salmon::XmlPayload do let(:entity) { Entities::TestEntity.new(test: "asdf") } - let(:payload) { Salmon::XmlPayload.pack(entity) } let(:entity_xml) { <<-XML.strip } @@ -12,48 +11,11 @@ module DiasporaFederation XML - describe ".pack" do - it "expects an Entity as param" do - expect { - Salmon::XmlPayload.pack(entity) - }.not_to raise_error - end - - it "raises an error when the param is not an Entity" do - ["asdf", 1234, true, :test, payload].each do |val| - expect { - Salmon::XmlPayload.pack(val) - }.to raise_error ArgumentError - end - end - - context "returned xml" do - subject { Salmon::XmlPayload.pack(entity) } - - it "returns an xml wrapper" do - expect(subject).to be_an_instance_of Nokogiri::XML::Element - expect(subject.name).to eq("XML") - expect(subject.children).to have(1).item - expect(subject.children[0].name).to eq("post") - expect(subject.children[0].children).to have(1).item - end - - it "returns the entity xml inside the wrapper" do - expect(subject.children[0].children[0].name).to eq("test_entity") - expect(subject.children[0].children[0].children).to have(1).item - end - - it "produces the expected XML" do - expect(subject.to_xml).to eq(entity_xml) - end - end - end - describe ".unpack" do context "sanity" do it "expects an Nokogiri::XML::Element as param" do expect { - Salmon::XmlPayload.unpack(payload) + Salmon::XmlPayload.unpack(Nokogiri::XML(entity_xml).root) }.not_to raise_error end @@ -67,7 +29,7 @@ XML end context "returned object" do - subject { Salmon::XmlPayload.unpack(Nokogiri::XML::Document.parse(entity_xml).root) } + 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) @@ -85,7 +47,7 @@ XML XML - entity = Salmon::XmlPayload.unpack(Nokogiri::XML::Document.parse(xml).root) + entity = Salmon::XmlPayload.unpack(Nokogiri::XML(xml).root) expect(entity).to be_an_instance_of Entities::TestEntity expect(entity.test).to eq("asdf") diff --git a/spec/lib/diaspora_federation/validators/relayable_retraction_validator_spec.rb b/spec/lib/diaspora_federation/validators/relayable_retraction_validator_spec.rb deleted file mode 100644 index 6d1495c..0000000 --- a/spec/lib/diaspora_federation/validators/relayable_retraction_validator_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -module DiasporaFederation - describe Validators::RelayableRetractionValidator do - let(:entity) { :relayable_retraction_entity } - it_behaves_like "a common validator" - - it_behaves_like "a diaspora* ID validator" do - let(:property) { :author } - let(:mandatory) { true } - end - - it_behaves_like "a guid validator" do - let(:property) { :target_guid } - end - - describe "#target_type" do - it_behaves_like "a property that mustn't be empty" do - let(:property) { :target_type } - end - end - - describe "#target" do - it_behaves_like "a property with a value validation/restriction" do - let(:property) { :target } - let(:wrong_values) { [nil] } - let(:correct_values) { [Fabricate(:related_entity)] } - end - end - end -end diff --git a/spec/lib/diaspora_federation/validators/request_validator_spec.rb b/spec/lib/diaspora_federation/validators/request_validator_spec.rb deleted file mode 100644 index 4c40fe7..0000000 --- a/spec/lib/diaspora_federation/validators/request_validator_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -module DiasporaFederation - describe Validators::RequestValidator do - let(:entity) { :request_entity } - - it_behaves_like "a common validator" - - %i(author recipient).each do |prop| - describe "##{prop}" do - it_behaves_like "a diaspora* ID validator" do - let(:property) { prop } - let(:mandatory) { true } - end - end - end - end -end diff --git a/spec/lib/diaspora_federation/validators/signed_retraction_validator_spec.rb b/spec/lib/diaspora_federation/validators/signed_retraction_validator_spec.rb deleted file mode 100644 index 27e9fcf..0000000 --- a/spec/lib/diaspora_federation/validators/signed_retraction_validator_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -module DiasporaFederation - describe Validators::SignedRetractionValidator do - let(:entity) { :signed_retraction_entity } - it_behaves_like "a common validator" - - it_behaves_like "a diaspora* ID validator" do - let(:property) { :author } - let(:mandatory) { true } - end - - it_behaves_like "a guid validator" do - let(:property) { :target_guid } - end - - describe "#target_type" do - it_behaves_like "a property that mustn't be empty" do - let(:property) { :target_type } - end - end - - describe "#target" do - it_behaves_like "a property with a value validation/restriction" do - let(:property) { :target } - let(:wrong_values) { [nil] } - let(:correct_values) { [Fabricate(:related_entity)] } - end - end - end -end diff --git a/spec/support/legacy_helper.rb b/spec/support/legacy_helper.rb new file mode 100644 index 0000000..384f43b --- /dev/null +++ b/spec/support/legacy_helper.rb @@ -0,0 +1,66 @@ +# 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 diff --git a/spec/support/shared_entity_specs.rb b/spec/support/shared_entity_specs.rb index 2191dd1..66c97f3 100644 --- a/spec/support/shared_entity_specs.rb +++ b/spec/support/shared_entity_specs.rb @@ -63,7 +63,7 @@ shared_examples "an XML Entity" do |ignored_props=[]| context "parsing" do it "reads its own output" do - packed_xml = DiasporaFederation::Salmon::XmlPayload.pack(instance) + packed_xml = instance.to_xml parsed_instance = DiasporaFederation::Salmon::XmlPayload.unpack(packed_xml) check_entity(instance, parsed_instance, ignored_props) @@ -106,12 +106,13 @@ shared_examples "a relayable Entity" do end it "computes correct signatures for the entity" do - signed_string = described_class::LEGACY_SIGNATURE_ORDER.map {|name| data[name] }.join(";") + order = described_class.class_props.keys - %i(author_signature parent_author_signature parent created_at) + signed_string = order.map {|name| data[name] }.join(";") - xml = DiasporaFederation::Salmon::XmlPayload.pack(instance) + xml = instance.to_xml - author_signature = xml.at_xpath("post/*[1]/author_signature").text - parent_author_signature = xml.at_xpath("post/*[1]/parent_author_signature").text + author_signature = xml.at_xpath("author_signature").text + parent_author_signature = xml.at_xpath("parent_author_signature").text expect(verify_signature(alice.public_key, author_signature, signed_string)).to be_truthy expect(verify_signature(bob.public_key, parent_author_signature, signed_string)).to be_truthy @@ -119,21 +120,6 @@ shared_examples "a relayable Entity" do end end -shared_examples "a retraction" do - context "receive with no target found" do - let(:unknown_guid) { Fabricate.sequence(:guid) } - let(:instance) { described_class.new(data.merge(target_guid: unknown_guid)) } - - it "raises when no target is found" do - xml = instance.to_xml - expect { - described_class.from_xml(xml) - }.to raise_error DiasporaFederation::Entities::Retraction::TargetNotFound, - "not found: #{data[:target_type]}:#{unknown_guid}" - end - end -end - shared_examples "a JSON Entity" do describe "#to_json" do it "#to_json output matches JSON schema" do