improve documentation

This commit is contained in:
Benjamin Neff 2015-11-29 23:21:18 +01:00
parent eef6ca19d2
commit dc0f786b2c
8 changed files with 70 additions and 24 deletions

View file

@ -32,6 +32,8 @@ module DiasporaFederation
end end
# Generates XML and updates signatures # Generates XML and updates signatures
# @see Entity#to_xml
# @return [Nokogiri::XML::Element] root element containing properties as child elements
def to_xml def to_xml
entity_xml.tap do |xml| entity_xml.tap do |xml|
hash = to_h hash = to_h

View file

@ -76,7 +76,7 @@ module DiasporaFederation
# {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Element Nokogiri::XML::Element}s # {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Element Nokogiri::XML::Element}s
# #
# @see Nokogiri::XML::Node.to_xml # @see Nokogiri::XML::Node.to_xml
# @see Salmon::XmlPayload.pack # @see XmlPayload#pack
# #
# @return [Nokogiri::XML::Element] root element containing properties as child elements # @return [Nokogiri::XML::Element] root element containing properties as child elements
def to_xml def to_xml

View file

@ -18,8 +18,11 @@ module DiasporaFederation
# @param [String] key AES key # @param [String] key AES key
# @param [String] iv AES initialization vector # @param [String] iv AES initialization vector
# @return [String] base64 encoded ciphertext # @return [String] base64 encoded ciphertext
# @raise [ArgumentError] if any of the arguments is missing or not the correct type
def self.encrypt(data, key, iv) def self.encrypt(data, key, iv)
raise ArgumentError unless data.instance_of?(String) raise ArgumentError unless data.instance_of?(String) &&
key.instance_of?(String) &&
iv.instance_of?(String)
cipher = OpenSSL::Cipher.new(CIPHER) cipher = OpenSSL::Cipher.new(CIPHER)
cipher.encrypt cipher.encrypt

View file

@ -41,8 +41,8 @@ module DiasporaFederation
# Creates a new instance of MagicEnvelope. # Creates a new instance of MagicEnvelope.
# #
# @param rsa_pkey [OpenSSL::PKey::RSA] private key used for signing # @param [OpenSSL::PKey::RSA] rsa_pkey private key used for signing
# @param payload [Entity] Entity instance # @param [Entity] payload Entity instance
# @raise [ArgumentError] if either argument is not of the right type # @raise [ArgumentError] if either argument is not of the right type
def initialize(rsa_pkey, payload) def initialize(rsa_pkey, payload)
raise ArgumentError unless rsa_pkey.instance_of?(OpenSSL::PKey::RSA) && raise ArgumentError unless rsa_pkey.instance_of?(OpenSSL::PKey::RSA) &&
@ -55,7 +55,7 @@ module DiasporaFederation
# Builds the XML structure for the magic envelope, inserts the {ENCODING} # Builds the XML structure for the magic envelope, inserts the {ENCODING}
# encoded data and signs the envelope using {DIGEST}. # encoded data and signs the envelope using {DIGEST}.
# #
# @return [Nokogiri::XML::Element] XML root node # @param [Nokogiri::XML::Builder] xml Salmon XML builder
def envelop(xml) def envelop(xml)
xml["me"].env { xml["me"].env {
xml["me"].data(Base64.urlsafe_encode64(@payload), type: DATA_TYPE) xml["me"].data(Base64.urlsafe_encode64(@payload), type: DATA_TYPE)
@ -71,7 +71,8 @@ module DiasporaFederation
# This must happen after the MagicEnvelope instance was created and before # This must happen after the MagicEnvelope instance was created and before
# {MagicEnvelope#envelop} is called. # {MagicEnvelope#envelop} is called.
# #
# @see Salmon.aes_encrypt # @see AES#generate_key_and_iv
# @see AES#encrypt
# #
# @return [Hash] AES key and iv. E.g.: { key: "...", iv: "..." } # @return [Hash] AES key and iv. E.g.: { key: "...", iv: "..." }
def encrypt! def encrypt!
@ -85,8 +86,8 @@ module DiasporaFederation
# #
# Does some sanity checking to avoid bad surprises... # Does some sanity checking to avoid bad surprises...
# #
# @see XmlPayload.unpack # @see XmlPayload#unpack
# @see Salmon.aes_decrypt # @see AES#decrypt
# #
# @param [Nokogiri::XML::Element] magic_env XML root node of a magic envelope # @param [Nokogiri::XML::Element] magic_env XML root node of a magic envelope
# @param [OpenSSL::PKey::RSA] rsa_pubkey public key to verify the signature # @param [OpenSSL::PKey::RSA] rsa_pubkey public key to verify the signature
@ -118,6 +119,8 @@ module DiasporaFederation
private private
# create the signature for all fields according to specification # create the signature for all fields according to specification
#
# @return [String] the signature
def signature def signature
subject = self.class.sig_subject([@payload, subject = self.class.sig_subject([@payload,
DATA_TYPE, DATA_TYPE,
@ -126,7 +129,7 @@ module DiasporaFederation
@rsa_pkey.sign(DIGEST, subject) @rsa_pkey.sign(DIGEST, subject)
end end
# @param [Nokogiri::XML::Element] env envelope # @param [Nokogiri::XML::Element] env magic envelope XML
def self.envelope_valid?(env) def self.envelope_valid?(env)
(env.instance_of?(Nokogiri::XML::Element) && (env.instance_of?(Nokogiri::XML::Element) &&
env.name == "env" && env.name == "env" &&
@ -137,8 +140,9 @@ module DiasporaFederation
end end
private_class_method :envelope_valid? private_class_method :envelope_valid?
# @param [Nokogiri::XML::Element] env # @param [Nokogiri::XML::Element] env magic envelope XML
# @param [OpenSSL::PKey::RSA] pkey public key # @param [OpenSSL::PKey::RSA] pkey public key
# @return [Boolean]
def self.signature_valid?(env, pkey) def self.signature_valid?(env, pkey)
subject = sig_subject([Base64.urlsafe_decode64(env.at_xpath("me:data").content), subject = sig_subject([Base64.urlsafe_decode64(env.at_xpath("me:data").content),
env.at_xpath("me:data")["type"], env.at_xpath("me:data")["type"],
@ -154,20 +158,28 @@ module DiasporaFederation
# the given array should consist of the data, data_type (mimetype), encoding # the given array should consist of the data, data_type (mimetype), encoding
# and the algorithm # and the algorithm
# @param [Array<String>] data_arr # @param [Array<String>] data_arr
# @return [String] signature subject
def self.sig_subject(data_arr) def self.sig_subject(data_arr)
data_arr.map {|i| Base64.urlsafe_encode64(i) }.join(".") data_arr.map {|i| Base64.urlsafe_encode64(i) }.join(".")
end end
# @param [Nokogiri::XML::Element] magic_env magic envelope XML
# @return [Boolean]
def self.encoding_valid?(magic_env) def self.encoding_valid?(magic_env)
magic_env.at_xpath("me:encoding").content == ENCODING magic_env.at_xpath("me:encoding").content == ENCODING
end end
private_class_method :encoding_valid? private_class_method :encoding_valid?
# @param [Nokogiri::XML::Element] magic_env magic envelope XML
# @return [Boolean]
def self.algorithm_valid?(magic_env) def self.algorithm_valid?(magic_env)
magic_env.at_xpath("me:alg").content == ALGORITHM magic_env.at_xpath("me:alg").content == ALGORITHM
end end
private_class_method :algorithm_valid? private_class_method :algorithm_valid?
# @param [Nokogiri::XML::Element] magic_env magic envelope XML
# @param [Hash] cipher_params hash containing the key and iv
# @return [String] data
def self.read_and_decrypt_data(magic_env, cipher_params) def self.read_and_decrypt_data(magic_env, cipher_params)
data = Base64.urlsafe_decode64(magic_env.at_xpath("me:data").content) data = Base64.urlsafe_decode64(magic_env.at_xpath("me:data").content)
data = AES.decrypt(data, cipher_params[:key], cipher_params[:iv]) unless cipher_params.nil? data = AES.decrypt(data, cipher_params[:key], cipher_params[:iv]) unless cipher_params.nil?

View file

@ -93,6 +93,11 @@ module DiasporaFederation
end end
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 def self.build_xml
builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml| builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
xml.diaspora("xmlns" => Salmon::XMLNS, "xmlns:me" => MagicEnvelope::XMLNS) { xml.diaspora("xmlns" => Salmon::XMLNS, "xmlns:me" => MagicEnvelope::XMLNS) {
@ -102,6 +107,9 @@ module DiasporaFederation
builder.to_xml builder.to_xml
end end
# Parses the magic envelop from the document.
#
# @param [Nokogiri::XML::Document] doc Salmon XML Document
def add_magic_env_from_doc(doc) def add_magic_env_from_doc(doc)
@magic_envelope = doc.at_xpath("d:diaspora/me:env", Slap::NS).tap do |env| @magic_envelope = doc.at_xpath("d:diaspora/me:env", Slap::NS).tap do |env|
raise MissingMagicEnvelope if env.nil? raise MissingMagicEnvelope if env.nil?

View file

@ -82,18 +82,18 @@ module DiasporaFederation
# Works recursively on nested Entities and Arrays thereof. # Works recursively on nested Entities and Arrays thereof.
# #
# @param [Class] klass entity class # @param [Class] klass entity class
# @param [Nokogiri::XML::Element] node xml nodes # @param [Nokogiri::XML::Element] root_node xml nodes
# @return [Entity] instance # @return [Entity] instance
def self.populate_entity(klass, node) def self.populate_entity(klass, root_node)
# Use all known properties to build the Entity. All other elements are respected # Use all known properties to build the Entity. All other elements are respected
# and attached to resulted hash as string. It is intended to build a hash # and attached to resulted hash as string. It is intended to build a hash
# invariable of an Entity definition, in order to support receiving objects # invariable of an Entity definition, in order to support receiving objects
# from the future versions of Diaspora, where new elements may have been added. # from the future versions of Diaspora, where new elements may have been added.
data = node.element_children.map { |child| data = root_node.element_children.map { |child|
xml_name = child.name xml_name = child.name
property = klass.class_props.find {|prop| prop[:xml_name].to_s == xml_name } property = klass.class_props.find {|prop| prop[:xml_name].to_s == xml_name }
if property if property
parse_element_from_node(property[:name], property[:type], xml_name, node) parse_element_from_node(property[:name], property[:type], xml_name, root_node)
else else
[xml_name, child.text] [xml_name, child.text]
end end
@ -105,6 +105,11 @@ module DiasporaFederation
end end
private_class_method :populate_entity private_class_method :populate_entity
# @param [Symbol] name property name
# @param [Class] type target type to parse
# @param [String] xml_name xml tag to parse
# @param [Nokogiri::XML::Element] node XML node to parse
# @return [Array<Symbol, Object>] parsed data
def self.parse_element_from_node(name, type, xml_name, node) def self.parse_element_from_node(name, type, xml_name, node)
if type == String if type == String
[name, parse_string_from_node(xml_name, node)] [name, parse_string_from_node(xml_name, node)]
@ -117,26 +122,35 @@ module DiasporaFederation
private_class_method :parse_element_from_node private_class_method :parse_element_from_node
# create simple entry in data hash # create simple entry in data hash
#
# @param [String] name xml tag to parse
# @param [Nokogiri::XML::Element] root_node XML root_node to parse
# @return [String] data # @return [String] data
def self.parse_string_from_node(name, node) def self.parse_string_from_node(name, root_node)
n = node.xpath(name.to_s) node = root_node.xpath(name.to_s)
n.first.text if n.any? node.first.text if node.any?
end end
private_class_method :parse_string_from_node private_class_method :parse_string_from_node
# create an entry in the data hash for the nested entity # create an entry in the data hash for the nested entity
#
# @param [Class] type target type to parse
# @param [Nokogiri::XML::Element] root_node XML node to parse
# @return [Entity] parsed child entity # @return [Entity] parsed child entity
def self.parse_entity_from_node(type, node) def self.parse_entity_from_node(type, root_node)
n = node.xpath(type.entity_name) node = root_node.xpath(type.entity_name)
populate_entity(type, n.first) if n.any? populate_entity(type, node.first) if node.any?
end end
private_class_method :parse_entity_from_node private_class_method :parse_entity_from_node
# collect all nested children of that type and create an array in the data hash # collect all nested children of that type and create an array in the data hash
#
# @param [Array<Class>] type target type to parse
# @param [Nokogiri::XML::Element] root_node XML node to parse
# @return [Array<Entity>] array with parsed child entities # @return [Array<Entity>] array with parsed child entities
def self.parse_array_from_node(type, node) def self.parse_array_from_node(type, root_node)
n = node.xpath(type.first.entity_name) node = root_node.xpath(type.first.entity_name)
n.map {|child| populate_entity(type.first, child) } node.map {|child| populate_entity(type.first, child) }
end end
private_class_method :parse_array_from_node private_class_method :parse_array_from_node
end end

View file

@ -2,6 +2,10 @@ module DiasporaFederation
# this module defines operations of signing an arbitrary hash with an arbitrary key # this module defines operations of signing an arbitrary hash with an arbitrary key
module Signing module Signing
extend Logging extend Logging
# Sign the data with the key
#
# @param [Hash] hash data to sign
# @param [OpenSSL::PKey::RSA] key An RSA key # @param [OpenSSL::PKey::RSA] key An RSA key
# @return [String] A Base64 encoded signature of #signable_string with key # @return [String] A Base64 encoded signature of #signable_string with key
def self.sign_with_key(hash, key) def self.sign_with_key(hash, key)
@ -17,6 +21,7 @@ module DiasporaFederation
# Check that signature is a correct signature # Check that signature is a correct signature
# #
# @param [Hash] hash data to verify
# @param [String] signature The signature to be verified. # @param [String] signature The signature to be verified.
# @param [OpenSSL::PKey::RSA] key An RSA key # @param [OpenSSL::PKey::RSA] key An RSA key
# @return [Boolean] # @return [Boolean]
@ -40,6 +45,8 @@ module DiasporaFederation
private private
# @param [Hash] hash data to sign
# @return [String] signature data string
def self.signable_string(hash) def self.signable_string(hash)
hash.map { |name, value| hash.map { |name, value|
value.to_s unless name.match(/signature/) value.to_s unless name.match(/signature/)

View file

@ -1,7 +1,7 @@
require "diaspora_federation/test/factories" require "diaspora_federation/test/factories"
module DiasporaFederation module DiasporaFederation
# This module incapsulates helper functions maybe wanted by a testsuite of a diaspora_federation gem user application # This module encapsulates helper functions maybe wanted by a testsuite of a diaspora_federation gem user application
module Test module Test
# Sort hash according to an entity class's property sequence. # Sort hash according to an entity class's property sequence.
# This is used for rspec tests in order to generate correct input hash to # This is used for rspec tests in order to generate correct input hash to