From f86044ddb2e4259f19bf924ddd73a354caabb41c Mon Sep 17 00:00:00 2001 From: Maximilian Schulz Date: Wed, 30 Mar 2016 18:44:49 +0200 Subject: [PATCH 1/5] Add support for HVU and HVZ order types --- lib/epics/client.rb | 44 ++++++++++++++++ lib/epics/h004.rb | 27 ++++++++++ lib/epics/h004/hvu.rb | 48 +++++++++++++++++ lib/epics/h004/hvz.rb | 58 +++++++++++++++++++++ lib/epics/hvu.rb | 41 +++++++++++++++ lib/epics/hvz.rb | 41 +++++++++++++++ spec/fixtures/xml/hvu_response.xml | 53 +++++++++++++++++++ spec/fixtures/xml/hvz_response.xml | 65 +++++++++++++++++++++++ spec/orders/hvu_spec.rb | 84 ++++++++++++++++++++++++++++++ spec/orders/hvz_spec.rb | 83 +++++++++++++++++++++++++++++ 10 files changed, 544 insertions(+) create mode 100644 lib/epics/h004.rb create mode 100644 lib/epics/h004/hvu.rb create mode 100644 lib/epics/h004/hvz.rb create mode 100644 lib/epics/hvu.rb create mode 100644 lib/epics/hvz.rb create mode 100644 spec/fixtures/xml/hvu_response.xml create mode 100644 spec/fixtures/xml/hvz_response.xml create mode 100644 spec/orders/hvu_spec.rb create mode 100644 spec/orders/hvz_spec.rb diff --git a/lib/epics/client.rb b/lib/epics/client.rb index 3d341b9..831a4c2 100644 --- a/lib/epics/client.rb +++ b/lib/epics/client.rb @@ -1,3 +1,7 @@ +require_relative 'h004' +require_relative './hvu' +require_relative './hvz' + class Epics::Client extend Forwardable @@ -135,10 +139,12 @@ def C53(from, to) download_and_unzip(Epics::C53, from, to) end + # Abrufbare Auftragsdaten abholen def HAA Nokogiri::XML(download(Epics::HAA)).at_xpath("//xmlns:OrderTypes", xmlns: "urn:org:ebics:H004").content.split(/\s/) end + # Kunden- und Teilnehmerinformationen des Kunden abholen def HTD Nokogiri::XML(download(Epics::HTD)).tap do |htd| @iban ||= htd.at_xpath("//xmlns:AccountNumber[@international='true']", xmlns: "urn:org:ebics:H004").text @@ -147,22 +153,60 @@ def HTD end.to_xml end + # Bankparameter-Datei abholen def HPD download(Epics::HPD) end + # Kunden- und Teilnehmerinformationen des Kunden abholen def HKD download(Epics::HKD) end + # Protokolldatei abholen def PTK(from, to) download(Epics::PTK, from, to) end + # XML Protokolldatei abholen def HAC(from = nil, to = nil) download(Epics::HAC, from, to) end + # VEU related actions + + # Unterschriftsmappe einsehen + def HVU + Epics::H004.from_xml(download(Epics::HVU)).to_h + end + + # Übersicht mit Zusatzinformationen abrufen (enthält Unterzeichner + Unterschriftsklasse) + def HVZ + Epics::H004.from_xml(download(Epics::HVZ)).to_h + end + + # # Statusinformationen abholen + # def HVD + # download(Epics::HVD) + # end + + # # Transaktionsdetails abrufen + # def HVT + # download(Epics::HVT) + # end + # + # # Unterschrift einreichen + # def HVE + # download(Epics::HVE) + # end + # + # # Unterschrift stornieren + # def HVS + # download(Epics::HVS) + # end + + # More stuff + def save_keys(path) File.write(path, dump_keys) end diff --git a/lib/epics/h004.rb b/lib/epics/h004.rb new file mode 100644 index 0000000..80065ad --- /dev/null +++ b/lib/epics/h004.rb @@ -0,0 +1,27 @@ +require 'nokogiri' + +require_relative './h004/hvu' +require_relative './h004/hvz' + +module Epics + module H004 + UnknownInput = Class.new(ArgumentError) + + def self.from_xml(raw_xml) + doc = Nokogiri::XML(raw_xml).at_xpath('/*') + + unless doc.namespace.href == "urn:org:ebics:H004" + fail UnknownInput, "Unknown xml file contents" + end + + case doc.name + when "HVZResponseOrderData" + Epics::H004::HVZ.new(doc) + when "HVUResponseOrderData" + Epics::H004::HVU.new(doc) + end + rescue Nokogiri::XML::XPath::SyntaxError => ex + fail UnknownInput, "Invalid XML input data: #{ex.message}" + end + end +end diff --git a/lib/epics/h004/hvu.rb b/lib/epics/h004/hvu.rb new file mode 100644 index 0000000..4f9616a --- /dev/null +++ b/lib/epics/h004/hvu.rb @@ -0,0 +1,48 @@ +module Epics + module H004 + class HVU + attr_accessor :doc + + def initialize(xml_doc) + self.doc = xml_doc + end + + def to_h + doc.xpath("/h004:HVUResponseOrderData/h004:OrderDetails").map do |order| + { + order_id: order.at_xpath('./h004:OrderID').content, + order_type: order.at_xpath('./h004:OrderType').content, + originator: originator(order.at_xpath('./h004:OriginatorInfo')), + signers: signers(order.xpath('./h004:SignerInfo')), + required_signatures: order.at_xpath('./h004:SigningInfo')['NumSigRequired'].to_i, + applied_signatures: order.at_xpath('./h004:SigningInfo')['NumSigDone'].to_i, + ready_for_signature: order.at_xpath('./h004:SigningInfo')['readyToBeSigned'].to_s.downcase == 'true', + } + end + end + + private + + def originator(node) + { + name: node.at_xpath('./h004:Name').content.strip, + partner_id: node.at_xpath('./h004:PartnerID').content.strip, + user_id: node.at_xpath('./h004:UserID').content.strip, + timestamp: Time.parse(node.at_xpath('./h004:Timestamp').content), + } + end + + def signers(nodes) + nodes.map do |signer| + { + name: signer.at_xpath('./h004:Name').content.strip, + partner_id: signer.at_xpath('./h004:PartnerID').content.strip, + user_id: signer.at_xpath('./h004:UserID').content.strip, + signature_class: signer.at_xpath('./h004:Permission')['AuthorisationLevel'] + } + end + end + + end + end +end diff --git a/lib/epics/h004/hvz.rb b/lib/epics/h004/hvz.rb new file mode 100644 index 0000000..0d53993 --- /dev/null +++ b/lib/epics/h004/hvz.rb @@ -0,0 +1,58 @@ +require 'bigdecimal' + +module Epics + module H004 + class HVZ + attr_accessor :doc + + def initialize(xml_doc) + self.doc = xml_doc + end + + def to_h + doc.xpath("/h004:HVZResponseOrderData/h004:OrderDetails").map do |order| + { + order_id: order.at_xpath('./h004:OrderID').content, + order_type: order.at_xpath('./h004:OrderType').content, + originator: originator(order.at_xpath('./h004:OriginatorInfo')), + signers: signers(order.xpath('./h004:SignerInfo')), + required_signatures: order.at_xpath('./h004:SigningInfo')['NumSigRequired'].to_i, + applied_signatures: order.at_xpath('./h004:SigningInfo')['NumSigDone'].to_i, + ready_for_signature: as_boolean(order.at_xpath('./h004:SigningInfo')['readyToBeSigned']), + total_amount: BigDecimal.new(order.at_xpath('./h004:TotalAmount').content), + total_amount_type: as_boolean(order.at_xpath('./h004:TotalAmount')['isCredit']) ? 'credit' : 'debit', + total_orders: order.at_xpath('./h004:TotalOrders').content.to_i, + digest: order.at_xpath('./h004:DataDigest').content, + digest_signature_version: order.at_xpath('./h004:DataDigest')['SignatureVersion'], + } + end + end + + private + + def originator(node) + { + name: node.at_xpath('./h004:Name').content.strip, + partner_id: node.at_xpath('./h004:PartnerID').content.strip, + user_id: node.at_xpath('./h004:UserID').content.strip, + timestamp: Time.parse(node.at_xpath('./h004:Timestamp').content), + } + end + + def signers(nodes) + nodes.map do |signer| + { + name: signer.at_xpath('./h004:Name').content.strip, + partner_id: signer.at_xpath('./h004:PartnerID').content.strip, + user_id: signer.at_xpath('./h004:UserID').content.strip, + signature_class: signer.at_xpath('./h004:Permission')['AuthorisationLevel'] + } + end + end + + def as_boolean(input) + input.to_s.downcase == 'true' + end + end + end +end diff --git a/lib/epics/hvu.rb b/lib/epics/hvu.rb new file mode 100644 index 0000000..7f72124 --- /dev/null +++ b/lib/epics/hvu.rb @@ -0,0 +1,41 @@ +module Epics + class HVU < Epics::GenericRequest + def header + { + :@authenticate => true, + static: { + "HostID" => host_id, + "Nonce" => nonce, + "Timestamp" => timestamp, + "PartnerID" => partner_id, + "UserID" => user_id, + "Product" => { + :@Language => "de", + :content! => "EPICS - a ruby ebics kernel" + }, + "OrderDetails" => { + "OrderType" => "HVU", + "OrderAttribute" => "DZHNN", + "HVUOrderParams" => {} + }, + "BankPubKeyDigests" => { + "Authentication" => { + :@Version => "X002", + :@Algorithm => "http://www.w3.org/2001/04/xmlenc#sha256", + :content! => client.bank_x.public_digest + }, + "Encryption" => { + :@Version => "E002", + :@Algorithm => "http://www.w3.org/2001/04/xmlenc#sha256", + :content! => client.bank_e.public_digest + } + }, + "SecurityMedium" => "0000" + }, + "mutable" => { + "TransactionPhase" => "Initialisation" + } + } + end + end +end diff --git a/lib/epics/hvz.rb b/lib/epics/hvz.rb new file mode 100644 index 0000000..d8a57c5 --- /dev/null +++ b/lib/epics/hvz.rb @@ -0,0 +1,41 @@ +module Epics + class HVZ < Epics::GenericRequest + def header + { + :@authenticate => true, + static: { + "HostID" => host_id, + "Nonce" => nonce, + "Timestamp" => timestamp, + "PartnerID" => partner_id, + "UserID" => user_id, + "Product" => { + :@Language => "de", + :content! => "EPICS - a ruby ebics kernel" + }, + "OrderDetails" => { + "OrderType" => "HVZ", + "OrderAttribute" => "DZHNN", + "HVZOrderParams" => {} + }, + "BankPubKeyDigests" => { + "Authentication" => { + :@Version => "X002", + :@Algorithm => "http://www.w3.org/2001/04/xmlenc#sha256", + :content! => client.bank_x.public_digest + }, + "Encryption" => { + :@Version => "E002", + :@Algorithm => "http://www.w3.org/2001/04/xmlenc#sha256", + :content! => client.bank_e.public_digest + } + }, + "SecurityMedium" => "0000" + }, + "mutable" => { + "TransactionPhase" => "Initialisation" + } + } + end + end +end diff --git a/spec/fixtures/xml/hvu_response.xml b/spec/fixtures/xml/hvu_response.xml new file mode 100644 index 0000000..ea1f999 --- /dev/null +++ b/spec/fixtures/xml/hvu_response.xml @@ -0,0 +1,53 @@ + + + + CCT + B03N + 1776 + + + RS + RST + + RS T + 2016-03-23T08:56:39.000000Z + + + + RS + RSA + + RS A + 2016-03-23T09:26:33.000000Z + + + + RS + RST + + RS T + 2016-03-23T08:56:39.000000Z + + + + CCT + B03P + 1776 + + + RS + RST + + RS T + 2016-03-29T10:57:12.000000Z + + + + RS + RST + + RS T + 2016-03-29T10:57:11.000000Z + + + diff --git a/spec/fixtures/xml/hvz_response.xml b/spec/fixtures/xml/hvz_response.xml new file mode 100644 index 0000000..a50599b --- /dev/null +++ b/spec/fixtures/xml/hvz_response.xml @@ -0,0 +1,65 @@ + + + + CCT + B03N + Ej9Q5zvpef87V9Ef5vdEY/aiPvEiVYupcZHir51G94g= + true + 1776 + false + 1 + 1000.13 + EUR + + + RS + RST + + RS T + 2016-03-23T08:56:39.000000Z + + + + RS + RSA + + RS A + 2016-03-23T09:26:33.000000Z + + + + RS + RST + + RS T + 2016-03-23T08:56:39.000000Z + + + + CCT + B03P + INN6KG5EHVovPLsbzdnNbfL7GGneYztgIYxXlRQwwI4= + true + 1776 + false + 1 + 1000.13 + EUR + + + RS + RST + + RS T + 2016-03-29T10:57:12.000000Z + + + + RS + RST + + RS T + 2016-03-29T10:57:11.000000Z + + + diff --git a/spec/orders/hvu_spec.rb b/spec/orders/hvu_spec.rb new file mode 100644 index 0000000..f5e0f6b --- /dev/null +++ b/spec/orders/hvu_spec.rb @@ -0,0 +1,84 @@ +require 'spec_helper' + +RSpec.describe Epics::HVU do + let(:client) { Epics::Client.new( File.open(File.join( File.dirname(__FILE__), '..', 'fixtures', 'SIZBN001.key')), 'secret' , 'https://194.180.18.30/ebicsweb/ebicsweb', 'SIZBN001', 'EBIX', 'EBICS') } + + describe 'No data available' do + before { allow(client).to receive(:download).with(described_class).and_raise(Epics::Error::BusinessError, "090005") } + + it 'returns an empty array' + end + + describe 'data is available' do + let(:xml_response) { File.read(File.join(File.dirname(__FILE__), '../fixtures/xml/hvu_response.xml')) } + + before { allow(client).to receive(:download).with(described_class).and_return(xml_response) } + + it 'parses and returns an array' do + expect(client.HVU).to be_instance_of(Array) + end + + it 'parses and returns each order' do + expect(client.HVU.size).to eq(2) + end + + describe 'attributes' do + subject { client.HVU.first } + + it 'sets the order id' do + expect(subject[:order_id]).to eq('B03N') + end + + it 'sets order type' do + expect(subject[:order_type]).to eq('CCT') + end + + it 'lists all signers' do + expect(subject[:signers]).to be_instance_of(Array) + end + + it 'sets the originator' do + expect(subject[:originator]).to match(hash_including( + :name => "RS T", + :partner_id => "RS", + :timestamp => Time.new(2016, 3, 23, 8, 56, 39, "+00:00"), + :user_id => "RST", + )) + end + + it 'sets flag if ready to be signed' do + expect(subject[:ready_for_signature]).to eq(true) + end + + it 'sets number of already applied signatures' do + expect(subject[:applied_signatures]).to eq(1) + end + + it 'sets number of required signatures' do + expect(subject[:required_signatures]).to eq(1) + end + + it 'does not have total_amount' do + expect(subject).to_not have_key(:total_amount) + end + + it 'does not have total_amount_type' do + expect(subject).to_not have_key(:total_amount_type) + end + + it 'does not have total_orders' do + expect(subject).to_not have_key(:total_orders) + end + + it 'does not have digest' do + expect(subject).to_not have_key(:digest) + end + + it 'does not have digest_signature_version' do + expect(subject).to_not have_key(:digest_signature_version) + end + end + + end + +end diff --git a/spec/orders/hvz_spec.rb b/spec/orders/hvz_spec.rb new file mode 100644 index 0000000..7bf00a9 --- /dev/null +++ b/spec/orders/hvz_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +RSpec.describe Epics::HVZ do + let(:client) { Epics::Client.new( File.open(File.join( File.dirname(__FILE__), '..', 'fixtures', 'SIZBN001.key')), 'secret' , 'https://194.180.18.30/ebicsweb/ebicsweb', 'SIZBN001', 'EBIX', 'EBICS') } + + describe 'No data available' do + before { allow(client).to receive(:download).with(Epics::HVZ).and_raise(Epics::Error::BusinessError, "090005") } + + it 'returns an empty array' + end + + describe 'data is available' do + let(:xml_response) { File.read(File.join(File.dirname(__FILE__), '../fixtures/xml/hvz_response.xml')) } + + before { allow(client).to receive(:download).with(Epics::HVZ).and_return(xml_response) } + + it 'parses and returns an array' do + expect(client.HVZ).to be_instance_of(Array) + end + + it 'parses and returns each order' do + expect(client.HVZ.size).to eq(2) + end + + describe 'attributes' do + subject { client.HVZ.first } + + it 'sets the order id' do + expect(subject[:order_id]).to eq('B03N') + end + + it 'sets order type' do + expect(subject[:order_type]).to eq('CCT') + end + + it 'lists all signers' do + expect(subject[:signers]).to be_instance_of(Array) + end + + it 'sets the originator' do + expect(subject[:originator]).to match(hash_including( + :name => "RS T", + :partner_id => "RS", + :timestamp => Time.new(2016, 3, 23, 8, 56, 39, "+00:00"), + :user_id => "RST", + )) + end + + it 'sets flag if ready to be signed' do + expect(subject[:ready_for_signature]).to eq(true) + end + + it 'sets number of already applied signatures' do + expect(subject[:applied_signatures]).to eq(1) + end + + it 'sets number of required signatures' do + expect(subject[:required_signatures]).to eq(1) + end + + it 'sets total_amount' do + expect(subject[:total_amount]).to eq(1000.13) + end + it 'sets total_amount_type' do + expect(subject[:total_amount_type]).to eq('credit') + end + + it 'sets total_orders' do + expect(subject[:total_orders]).to eq(1) + end + + it 'sets digest' do + expect(subject[:digest]).to eq('Ej9Q5zvpef87V9Ef5vdEY/aiPvEiVYupcZHir51G94g=') + end + + it 'sets digest_signature_version' do + expect(subject[:digest_signature_version]).to eq('A006') + end + end + + end + +end From b9d3ba056ba3e3982692b797e46d1ada23ac2b33 Mon Sep 17 00:00:00 2001 From: Maximilian Schulz Date: Thu, 31 Mar 2016 11:05:34 +0200 Subject: [PATCH 2/5] Implement fetching of transaction details via HVD In order to perform this kind of request, we need the order id and its order type. --- lib/epics/client.rb | 9 ++-- lib/epics/h004.rb | 3 ++ lib/epics/h004/hvd.rb | 46 +++++++++++++++++ lib/epics/hvd.rb | 53 +++++++++++++++++++ spec/fixtures/xml/hvd_response.xml | 24 +++++++++ spec/orders/hvd_spec.rb | 83 ++++++++++++++++++++++++++++++ 6 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 lib/epics/h004/hvd.rb create mode 100644 lib/epics/hvd.rb create mode 100644 spec/fixtures/xml/hvd_response.xml create mode 100644 spec/orders/hvd_spec.rb diff --git a/lib/epics/client.rb b/lib/epics/client.rb index 831a4c2..d031de6 100644 --- a/lib/epics/client.rb +++ b/lib/epics/client.rb @@ -1,6 +1,7 @@ require_relative 'h004' require_relative './hvu' require_relative './hvz' +require_relative './hvd' class Epics::Client extend Forwardable @@ -185,10 +186,10 @@ def HVZ Epics::H004.from_xml(download(Epics::HVZ)).to_h end - # # Statusinformationen abholen - # def HVD - # download(Epics::HVD) - # end + # Statusinformationen abholen + def HVD(order_id, order_type) + Epics::H004.from_xml(download(Epics::HVD, order_id, order_type)).to_h + end # # Transaktionsdetails abrufen # def HVT diff --git a/lib/epics/h004.rb b/lib/epics/h004.rb index 80065ad..34cc888 100644 --- a/lib/epics/h004.rb +++ b/lib/epics/h004.rb @@ -2,6 +2,7 @@ require_relative './h004/hvu' require_relative './h004/hvz' +require_relative './h004/hvd' module Epics module H004 @@ -19,6 +20,8 @@ def self.from_xml(raw_xml) Epics::H004::HVZ.new(doc) when "HVUResponseOrderData" Epics::H004::HVU.new(doc) + when "HVDResponseOrderData" + Epics::H004::HVD.new(doc) end rescue Nokogiri::XML::XPath::SyntaxError => ex fail UnknownInput, "Invalid XML input data: #{ex.message}" diff --git a/lib/epics/h004/hvd.rb b/lib/epics/h004/hvd.rb new file mode 100644 index 0000000..cadfd72 --- /dev/null +++ b/lib/epics/h004/hvd.rb @@ -0,0 +1,46 @@ +module Epics + module H004 + class HVD + attr_accessor :doc + + def initialize(xml_doc) + self.doc = xml_doc + end + + def to_h + order = doc.at_xpath("/h004:HVDResponseOrderData") + { + signers: signers(order.xpath('./h004:SignerInfo')), + order_details_available: order.at_xpath('./h004:OrderDetailsAvailable').content == 'true', + order_data_available: order.at_xpath('./h004:OrderDataAvailable').content == 'true', + digest: order.at_xpath('./h004:DataDigest').content, + digest_signature_version: order.at_xpath('./h004:DataDigest')['SignatureVersion'], + display_file: Base64.decode64(order.at_xpath('./h004:DisplayFile').content).encode("UTF-8", "ISO-8859-15"), + } + end + + private + + def originator(node) + { + name: node.at_xpath('./h004:Name').content.strip, + partner_id: node.at_xpath('./h004:PartnerID').content.strip, + user_id: node.at_xpath('./h004:UserID').content.strip, + timestamp: Time.parse(node.at_xpath('./h004:Timestamp').content), + } + end + + def signers(nodes) + nodes.map do |signer| + { + name: signer.at_xpath('./h004:Name').content.strip, + partner_id: signer.at_xpath('./h004:PartnerID').content.strip, + user_id: signer.at_xpath('./h004:UserID').content.strip, + signature_class: signer.at_xpath('./h004:Permission')['AuthorisationLevel'] + } + end + end + + end + end +end diff --git a/lib/epics/hvd.rb b/lib/epics/hvd.rb new file mode 100644 index 0000000..345578f --- /dev/null +++ b/lib/epics/hvd.rb @@ -0,0 +1,53 @@ +module Epics + class HVD < Epics::GenericRequest + attr_accessor :order_id, :order_type + + def initialize(client, order_id, order_type) + super(client) + self.order_id = order_id + self.order_type = order_type + end + + def header + { + :@authenticate => true, + static: { + "HostID" => host_id, + "Nonce" => nonce, + "Timestamp" => timestamp, + "PartnerID" => partner_id, + "UserID" => user_id, + "Product" => { + :@Language => "de", + :content! => "EPICS - a ruby ebics kernel" + }, + "OrderDetails" => { + "OrderType" => "HVD", + "OrderAttribute" => "DZHNN", + "HVDOrderParams" => { + "PartnerID" => partner_id, + "OrderType" => order_type, + "OrderID" => order_id, + } + }, + "BankPubKeyDigests" => { + "Authentication" => { + :@Version => "X002", + :@Algorithm => "http://www.w3.org/2001/04/xmlenc#sha256", + :content! => client.bank_x.public_digest + }, + "Encryption" => { + :@Version => "E002", + :@Algorithm => "http://www.w3.org/2001/04/xmlenc#sha256", + :content! => client.bank_e.public_digest + } + }, + "SecurityMedium" => "0000" + }, + "mutable" => { + "TransactionPhase" => "Initialisation" + } + } + end + end +end diff --git a/spec/fixtures/xml/hvd_response.xml b/spec/fixtures/xml/hvd_response.xml new file mode 100644 index 0000000..23325bb --- /dev/null +++ b/spec/fixtures/xml/hvd_response.xml @@ -0,0 +1,24 @@ + + + Ej9Q5zvpef87V9Ef5vdEY/aiPvEiVYupcZHir51G94g= + DQogICAgICAgICA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiAgICAgICAgIEcgVSBUIFMgQyBIIFIgSSBGIFQgRSBOCiAgICAgICAgIERhdGVpLUlEICAgOiBFQklDUy1CT1gvQTk2MDI5ODg1MTQ4MzY2QUU5NTA4MgogICAgICAgICBEYXR1bS9aZWl0IDogMjMuMDMuMjAxNi8xMDo1NjozNgogICAgICAgICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgICAgICAgIFNhbW1sZXJyZWZlcmVueiAgICAgICAgICAgICAgOiBFQklDUy1CT1gvQTk2MDI5ODg1MTQ4MzY2QUU5NTA4Mi8xCiAgICAgICAgIEJhbmstQ29kZSAgICAgICAgICAgICAgICAgICAgOiBYQkFOREVDRwogICAgICAgICBLb250b251bW1lciAgICAgICAgICAgICAgICAgIDogREUzNjI1MDQwMDkwMDAwMTIzNDU1NQogICAgICAgICBBdWZ0cmFnZ2ViZXJkYXRlbiAgICAgICAgICAgIDogUUEgS29udG8gQlYKICAgICAgICAgQW56YWhsIGRlciBaYWhsdW5nc3PkdHplICAgICA6IDEKICAgICAgICAgU3VtbWUgZGVyIEJldHLkZ2UgKCBFVVIgKSAgICA6IDEuMDAwLDEzCiAgICAgICAgIEF1c2b8aHJ1bmdzdGVybWluICAgICAgICAgICAgOiAyMy4wMy4yMDE2CiAgICAgICAgID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0= + true + 1776 + false + + RS + RST + + RS T + 2016-03-23T08:56:39.000000Z + + + + RS + RSA + + RS A + 2016-03-23T09:26:33.000000Z + + + diff --git a/spec/orders/hvd_spec.rb b/spec/orders/hvd_spec.rb new file mode 100644 index 0000000..a67774e --- /dev/null +++ b/spec/orders/hvd_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +RSpec.describe Epics::HVD do + let(:client) { Epics::Client.new( File.open(File.join( File.dirname(__FILE__), '..', 'fixtures', 'SIZBN001.key')), 'secret' , 'https://194.180.18.30/ebicsweb/ebicsweb', 'SIZBN001', 'EBIX', 'EBICS') } + + describe 'No data available' do + it 'returns nil' + end + + describe 'data is available' do + let(:xml_response) { File.read(File.join(File.dirname(__FILE__), '../fixtures/xml/hvd_response.xml')) } + + before { allow(client).to receive(:download).with(described_class, 'B03N', 'CCT').and_return(xml_response) } + + describe 'attributes' do + subject { client.HVD('B03N', 'CCT') } + + it 'lists all signers' do + expect(subject[:signers]).to be_instance_of(Array) + end + + it 'indicates if detailed order data is available' do + expect(subject[:order_details_available]).to eq(false) + end + + it 'indicates if order data is available' do + expect(subject[:order_data_available]).to eq(true) + end + + it 'sets correct string encoding' do + expect(subject[:display_file]).to include("Ausführungstermin") + end + + it 'indicates if order data is available' do + expect(subject[:display_file]).to include("Datei-ID") + end + + it 'sets digest' do + expect(subject[:digest]).to eq('Ej9Q5zvpef87V9Ef5vdEY/aiPvEiVYupcZHir51G94g=') + end + + it 'sets digest_signature_version' do + expect(subject[:digest_signature_version]).to eq('A006') + end + + it 'does not include an order id' do + expect(subject).to_not have_key(:order_id) + end + + it 'does not include an order type' do + expect(subject).to_not have_key(:order_type) + end + + it 'does not include an originator' do + expect(subject).to_not have_key(:originator) + end + + it 'does not include flag if ready to be signed' do + expect(subject).to_not have_key(:ready_for_signature) + end + + it 'does not include number of already applied signatures' do + expect(subject).to_not have_key(:applied_signatures) + end + + it 'does not include number of required signatures' do + expect(subject).to_not have_key(:required_signatures) + end + + it 'does not include not have total_amount' do + expect(subject).to_not have_key(:total_amount) + end + + it 'does not have total_amount_type' do + expect(subject).to_not have_key(:total_amount_type) + end + + it 'does not have total_orders' do + expect(subject).to_not have_key(:total_orders) + end + end + end +end From d5b92ec53cd212b48bc45812ebd084254644788f Mon Sep 17 00:00:00 2001 From: Maximilian Schulz Date: Thu, 31 Mar 2016 20:43:51 +0200 Subject: [PATCH 3/5] Add support for signing and rejecting ebics orders --- lib/epics/client.rb | 39 ++++++++---------- lib/epics/generic_upload_request.rb | 8 +++- lib/epics/hve.rb | 64 +++++++++++++++++++++++++++++ lib/epics/hvs.rb | 64 +++++++++++++++++++++++++++++ spec/generic_upload_spec.rb | 8 +++- spec/orders/hve_spec.rb | 42 +++++++++++++++++++ spec/orders/hvs_spec.rb | 42 +++++++++++++++++++ 7 files changed, 243 insertions(+), 24 deletions(-) create mode 100644 lib/epics/hve.rb create mode 100644 lib/epics/hvs.rb create mode 100644 spec/orders/hve_spec.rb create mode 100644 spec/orders/hvs_spec.rb diff --git a/lib/epics/client.rb b/lib/epics/client.rb index d031de6..17950f2 100644 --- a/lib/epics/client.rb +++ b/lib/epics/client.rb @@ -2,6 +2,8 @@ require_relative './hvu' require_relative './hvz' require_relative './hvd' +require_relative './hve' +require_relative './hvs' class Epics::Client extend Forwardable @@ -176,37 +178,30 @@ def HAC(from = nil, to = nil) # VEU related actions - # Unterschriftsmappe einsehen + # Fetch overview of all orders which need to be signed def HVU Epics::H004.from_xml(download(Epics::HVU)).to_h end - # Übersicht mit Zusatzinformationen abrufen (enthält Unterzeichner + Unterschriftsklasse) + # Fetch detailed overview of all orders which need to be signed def HVZ Epics::H004.from_xml(download(Epics::HVZ)).to_h end - # Statusinformationen abholen + # Fetch details for an order def HVD(order_id, order_type) Epics::H004.from_xml(download(Epics::HVD, order_id, order_type)).to_h end - # # Transaktionsdetails abrufen - # def HVT - # download(Epics::HVT) - # end - # - # # Unterschrift einreichen - # def HVE - # download(Epics::HVE) - # end - # - # # Unterschrift stornieren - # def HVS - # download(Epics::HVS) - # end + # sign an order + def HVE(order_id, order_type, digest) + upload(Epics::HVE, order_id, order_type, digest) + end - # More stuff + # reject an order + def HVS(order_id, order_type, digest) + upload(Epics::HVS, order_id, order_type, digest) + end def save_keys(path) File.write(path, dump_keys) @@ -214,12 +209,14 @@ def save_keys(path) private - def upload(order_type, document) - order = order_type.new(self, document) + def upload(order_type, *args) + order = order_type.new(self, *args) res = post(url, order.to_xml).body order.transaction_id = res.transaction_id - res = post(url, order.to_transfer_xml).body + if order.segmented? + res = post(url, order.to_transfer_xml).body + end return res.transaction_id, res.order_id end diff --git a/lib/epics/generic_upload_request.rb b/lib/epics/generic_upload_request.rb index 53636a1..75172b8 100644 --- a/lib/epics/generic_upload_request.rb +++ b/lib/epics/generic_upload_request.rb @@ -3,12 +3,16 @@ class Epics::GenericUploadRequest < Epics::GenericRequest attr_accessor :key attr_accessor :document - def initialize(client, document) + def initialize(client, document = nil) super(client) self.document = document self.key ||= cipher.random_key end + def segmented? + true + end + def cipher @cipher ||= OpenSSL::Cipher::Cipher.new("aes-128-cbc") end @@ -83,4 +87,4 @@ def pad(d) end end -end \ No newline at end of file +end diff --git a/lib/epics/hve.rb b/lib/epics/hve.rb new file mode 100644 index 0000000..42d82ab --- /dev/null +++ b/lib/epics/hve.rb @@ -0,0 +1,64 @@ +module Epics + class HVE < Epics::GenericUploadRequest + + attr_accessor :order_id, :order_type, :digest + + def initialize(client, order_id, order_type, digest) + super(client) + self.order_id = order_id + self.order_type = order_type + self.digest = digest + end + + def segmented? + false + end + + def header + { + :@authenticate => true, + static: { + "HostID" => host_id, + "Nonce" => nonce, + "Timestamp" => timestamp, + "PartnerID" => partner_id, + "UserID" => user_id, + "Product" => { + :@Language => "de", + :content! => "EPICS - a ruby ebics kernel" + }, + "OrderDetails" => { + "OrderType" => "HVE", + "OrderAttribute" => "UZHNN", + "HVEOrderParams" => { + "PartnerID" => partner_id, + "OrderType" => order_type, + "OrderID" => order_id, + } + }, + "BankPubKeyDigests" => { + "Authentication" => { + :@Version => "X002", + :@Algorithm => "http://www.w3.org/2001/04/xmlenc#sha256", + :content! => client.bank_x.public_digest + }, + "Encryption" => { + :@Version => "E002", + :@Algorithm => "http://www.w3.org/2001/04/xmlenc#sha256", + :content! => client.bank_e.public_digest + } + }, + "SecurityMedium" => "0000", + "NumSegments" => 0, + }, + "mutable" => { + "TransactionPhase" => "Initialisation" + } + } + end + + def signature_value + client.a.sign(Base64.strict_decode64(digest)) + end + end +end diff --git a/lib/epics/hvs.rb b/lib/epics/hvs.rb new file mode 100644 index 0000000..447df95 --- /dev/null +++ b/lib/epics/hvs.rb @@ -0,0 +1,64 @@ +module Epics + class HVS < Epics::GenericUploadRequest + + attr_accessor :order_id, :order_type, :digest + + def initialize(client, order_id, order_type, digest) + super(client) + self.order_id = order_id + self.order_type = order_type + self.digest = digest + end + + def segmented? + false + end + + def header + { + :@authenticate => true, + static: { + "HostID" => host_id, + "Nonce" => nonce, + "Timestamp" => timestamp, + "PartnerID" => partner_id, + "UserID" => user_id, + "Product" => { + :@Language => "de", + :content! => "EPICS - a ruby ebics kernel" + }, + "OrderDetails" => { + "OrderType" => "HVS", + "OrderAttribute" => "UZHNN", + "HVSOrderParams" => { + "PartnerID" => partner_id, + "OrderType" => order_type, + "OrderID" => order_id, + } + }, + "BankPubKeyDigests" => { + "Authentication" => { + :@Version => "X002", + :@Algorithm => "http://www.w3.org/2001/04/xmlenc#sha256", + :content! => client.bank_x.public_digest + }, + "Encryption" => { + :@Version => "E002", + :@Algorithm => "http://www.w3.org/2001/04/xmlenc#sha256", + :content! => client.bank_e.public_digest + } + }, + "SecurityMedium" => "0000", + "NumSegments" => 0, + }, + "mutable" => { + "TransactionPhase" => "Initialisation" + } + } + end + + def signature_value + client.a.sign(Base64.strict_decode64(digest)) + end + end +end diff --git a/spec/generic_upload_spec.rb b/spec/generic_upload_spec.rb index fef3bac..df709f8 100644 --- a/spec/generic_upload_spec.rb +++ b/spec/generic_upload_spec.rb @@ -2,6 +2,12 @@ let(:client) { Epics::Client.new( File.open(File.join( File.dirname(__FILE__), 'fixtures', 'SIZBN001.key')), 'secret' , 'https://194.180.18.30/ebicsweb/ebicsweb', 'SIZBN001', 'EBIX', 'EBICS') } subject { described_class.new(client, "\x01" * 12) } + describe '#segmented?' do + it 'returns true by default' do + expect(subject.segmented?).to eq(true) + end + end + describe '#pad' do it 'will complete the block to the next 16byte' do @@ -34,4 +40,4 @@ end end -end \ No newline at end of file +end diff --git a/spec/orders/hve_spec.rb b/spec/orders/hve_spec.rb new file mode 100644 index 0000000..c6248ef --- /dev/null +++ b/spec/orders/hve_spec.rb @@ -0,0 +1,42 @@ +RSpec.describe Epics::HVE do + let(:client) { Epics::Client.new( File.open(File.join( File.dirname(__FILE__), '..', 'fixtures', 'SIZBN001.key')), 'secret' , 'https://194.180.18.30/ebicsweb/ebicsweb', 'SIZBN001', 'EBIX', 'EBICS') } + let(:digest) { Base64.strict_encode64("supersecretdigest") } + + subject { described_class.new(client, "myid", "mytype", digest) } + + describe '#segmented?' do + it 'returns false' do + expect(subject.segmented?).to eq(false) + end + end + + describe '#to_xml' do + describe 'message header' do + it 'sets order type' do + expect(subject.to_xml).to include("HVE") + end + + it 'uploads as order without segments' do + expect(subject.to_xml).to include("UZHNN") + end + + it 'sets order params' do + expect(subject.to_xml).to include("") + end + + it 'sets number of segments to zero' do + expect(subject.to_xml).to include("0") + end + end + + describe 'message body' do + it 'includes a signature' do + expect(subject.to_xml).to include("") + end + + it 'does not have any order data' do + expect(subject.to_xml).to_not include("") + end + end + end +end diff --git a/spec/orders/hvs_spec.rb b/spec/orders/hvs_spec.rb new file mode 100644 index 0000000..538a352 --- /dev/null +++ b/spec/orders/hvs_spec.rb @@ -0,0 +1,42 @@ +RSpec.describe Epics::HVS do + let(:client) { Epics::Client.new( File.open(File.join( File.dirname(__FILE__), '..', 'fixtures', 'SIZBN001.key')), 'secret' , 'https://194.180.18.30/ebicsweb/ebicsweb', 'SIZBN001', 'EBIX', 'EBICS') } + let(:digest) { Base64.strict_encode64("supersecretdigest") } + + subject { described_class.new(client, "myid", "mytype", digest) } + + describe '#segmented?' do + it 'returns false' do + expect(subject.segmented?).to eq(false) + end + end + + describe '#to_xml' do + describe 'message header' do + it 'sets order type' do + expect(subject.to_xml).to include("HVS") + end + + it 'uploads as order without segments' do + expect(subject.to_xml).to include("UZHNN") + end + + it 'sets order params' do + expect(subject.to_xml).to include("") + end + + it 'sets number of segments to zero' do + expect(subject.to_xml).to include("0") + end + end + + describe 'message body' do + it 'includes a signature' do + expect(subject.to_xml).to include("") + end + + it 'does not have any order data' do + expect(subject.to_xml).to_not include("") + end + end + end +end From e9778ac4104bdf0395c11a51a99450a058358f70 Mon Sep 17 00:00:00 2001 From: Maximilian Schulz Date: Tue, 5 Apr 2016 11:39:15 +0200 Subject: [PATCH 4/5] Set file encoding for older ruby versions --- spec/orders/hvd_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/orders/hvd_spec.rb b/spec/orders/hvd_spec.rb index a67774e..f2be648 100644 --- a/spec/orders/hvd_spec.rb +++ b/spec/orders/hvd_spec.rb @@ -1,3 +1,5 @@ +# encoding: utf-8 + require 'spec_helper' RSpec.describe Epics::HVD do From 92dfc192d04bf9fe5d94ecba2c022630fec329a1 Mon Sep 17 00:00:00 2001 From: Maximilian Schulz Date: Tue, 5 Apr 2016 11:39:41 +0200 Subject: [PATCH 5/5] Update list of supported ruby versions / platforms --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 36201fb..9a94549 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,4 +3,6 @@ rvm: - 2.0.0 - 2.1.8 - 2.2.4 - - jruby + - 2.3.0 + - jruby-9.0.5.0 + - jruby-1.7.20