sepafm 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +14 -30
- data/.ruby-version +1 -1
- data/.travis.yml +15 -0
- data/Gemfile +1 -1
- data/LICENSE +16 -4
- data/README.md +180 -319
- data/Rakefile +7 -2
- data/lib/sepa/application_request.rb +100 -131
- data/lib/sepa/application_response.rb +28 -84
- data/lib/sepa/attribute_checks.rb +169 -0
- data/lib/sepa/banks/danske/danske_response.rb +19 -0
- data/lib/sepa/banks/danske/soap_danske.rb +132 -0
- data/lib/sepa/banks/nordea/nordea_response.rb +20 -0
- data/lib/sepa/banks/nordea/soap_nordea.rb +51 -0
- data/lib/sepa/client.rb +72 -60
- data/lib/sepa/error_messages.rb +15 -0
- data/lib/sepa/response.rb +88 -85
- data/lib/sepa/soap_builder.rb +51 -341
- data/lib/sepa/utilities.rb +132 -0
- data/lib/sepa/version.rb +1 -1
- data/lib/sepa/xml_schemas/PKIFactory.xsd +334 -0
- data/lib/sepa/xml_schemas/xml_id.xsd +9 -0
- data/lib/sepa/xml_templates/application_request/create_certificate.xml +15 -10
- data/lib/sepa/xml_templates/application_request/danske_get_bank_certificate.xml +13 -9
- data/lib/sepa/xml_templates/application_request/download_file.xml +32 -30
- data/lib/sepa/xml_templates/application_request/download_file_list.xml +29 -27
- data/lib/sepa/xml_templates/application_request/encrypted_request.xml +22 -0
- data/lib/sepa/xml_templates/application_request/get_certificate.xml +9 -8
- data/lib/sepa/xml_templates/application_request/get_user_info.xml +26 -24
- data/lib/sepa/xml_templates/application_request/upload_file.xml +29 -27
- data/lib/sepa/xml_templates/soap/create_certificate.xml +17 -15
- data/lib/sepa/xml_templates/soap/danske_get_bank_certificate.xml +15 -13
- data/lib/sepa/xml_templates/soap/download_file.xml +19 -15
- data/lib/sepa/xml_templates/soap/download_file_list.xml +19 -15
- data/lib/sepa/xml_templates/soap/get_certificate.xml +2 -1
- data/lib/sepa/xml_templates/soap/get_user_info.xml +19 -15
- data/lib/sepa/xml_templates/soap/header.xml +48 -37
- data/lib/sepa/xml_templates/soap/upload_file.xml +19 -15
- data/lib/sepafm.rb +20 -18
- data/{sepa.gemspec → sepafm.gemspec} +10 -9
- data/test/sepa/banks/danske/danske_cert_response_test.rb +52 -0
- data/test/sepa/banks/danske/danske_cert_soap_builder_test.rb +100 -0
- data/test/sepa/banks/danske/danske_generic_soap_builder_test.rb +278 -0
- data/test/sepa/banks/danske/danske_get_bank_cert_test.rb +111 -0
- data/{lib/sepa/danske_testing/keys/danske_encryption.crt → test/sepa/banks/danske/keys/bank_encryption_cert.pem} +0 -0
- data/test/sepa/{danske_test_keys/danskeroot.pem → banks/danske/keys/bank_root_cert.pem} +1 -1
- data/test/sepa/banks/danske/keys/bank_signing_cert.pem +24 -0
- data/test/sepa/banks/danske/keys/danske_encryption.crt +24 -0
- data/test/sepa/banks/danske/keys/enc_private_key.pem +27 -0
- data/test/sepa/{danske_test_keys → banks/danske/keys}/encryption_pkcs.csr +0 -0
- data/test/sepa/banks/danske/keys/own_enc_cert.pem +21 -0
- data/test/sepa/banks/danske/keys/own_signing_cert.pem +22 -0
- data/test/sepa/{danske_test_keys → banks/danske/keys}/signing_key.pem +0 -0
- data/test/sepa/{danske_test_keys → banks/danske/keys}/signing_pkcs.csr +0 -0
- data/test/sepa/banks/danske/keys/signing_private_key.pem +27 -0
- data/test/sepa/banks/danske/responses/create_cert.xml +38 -0
- data/test/sepa/banks/danske/responses/get_bank_cert.xml +37 -0
- data/{lib/sepa/nordea_testing → test/sepa/banks/nordea}/keys/nordea.crt +0 -4
- data/test/sepa/{nordea_test_keys → banks/nordea/keys}/nordea.key +0 -3
- data/test/sepa/{nordea_test_keys → banks/nordea/keys}/root_cert.cer +0 -0
- data/test/sepa/{nordea_test_keys → banks/nordea/keys}/testcert.csr +0 -0
- data/test/sepa/banks/nordea/nordea_application_request_test.rb +252 -0
- data/test/sepa/{application_response_test.rb → banks/nordea/nordea_application_response_test.rb} +40 -46
- data/test/sepa/banks/nordea/nordea_cert_application_request_test.rb +72 -0
- data/test/sepa/banks/nordea/nordea_cert_request_soap_builder_test.rb +65 -0
- data/test/sepa/banks/nordea/nordea_generic_soap_builder_test.rb +280 -0
- data/test/sepa/banks/nordea/nordea_response_test.rb +116 -0
- data/test/sepa/banks/nordea/responses/df_ktl.xml +45 -0
- data/test/sepa/{test_files/test_responses/df.xml → banks/nordea/responses/df_tito.xml} +1 -1
- data/test/sepa/{test_files/test_responses → banks/nordea/responses}/dfl.xml +0 -0
- data/test/sepa/banks/nordea/responses/gbc.xml +15 -0
- data/test/sepa/banks/nordea/responses/gc.xml +49 -0
- data/test/sepa/{test_files/test_responses → banks/nordea/responses}/gui.xml +0 -0
- data/test/sepa/{test_files/test_responses → banks/nordea/responses}/uf.xml +0 -0
- data/test/sepa/client_test.rb +156 -302
- data/test/sepa/fixtures.rb +214 -0
- data/test/sepa/sepa_test.rb +3 -13
- data/test/sepa/test_files/{invalid.wsdl → invalid_wsdl.wsdl} +0 -0
- data/test/test_helper.rb +29 -3
- metadata +140 -116
- data/lib/danske_get_bank_certificate_test.rb +0 -15
- data/lib/sepa/custom_exceptions.rb +0 -2
- data/lib/sepa/filedescriptor.rb +0 -7
- data/lib/sepa/filetypeservice.rb +0 -6
- data/lib/sepa/nordea_testing/keys/CSR.csr +0 -0
- data/lib/sepa/nordea_testing/keys/nordea.key +0 -19
- data/lib/sepa/nordea_testing/response/content_053.xml +0 -998
- data/lib/sepa/nordea_testing/response/content_054.xml +0 -1
- data/lib/sepa/nordea_testing/response/download_file_response.xml +0 -14
- data/lib/sepa/nordea_testing/response/download_filelist_response.xml +0 -14
- data/lib/sepa/nordea_testing/response/get_user_info_response.xml +0 -14
- data/lib/sepa/nordea_testing/response/upload_file_response.xml +0 -14
- data/lib/sepa/payload.rb +0 -109
- data/lib/sepa/payment.rb +0 -97
- data/lib/sepa/sender_verifier.rb +0 -15
- data/lib/sepa/signature.rb +0 -7
- data/lib/sepa/soap_danske.rb +0 -47
- data/lib/sepa/soap_nordea.rb +0 -68
- data/lib/sepa/transaction.rb +0 -178
- data/lib/sepa/userfiletype.rb +0 -16
- data/lib/sepa/xml_parser.rb +0 -291
- data/lib/sepa_client_testing_mika.rb +0 -32
- data/lib/sepa_client_testing_tiere.rb +0 -257
- data/test/sepa/application_request_test.rb +0 -423
- data/test/sepa/cert_application_request_test.rb +0 -99
- data/test/sepa/nordea_cert_request_soap_builder_test.rb +0 -112
- data/test/sepa/nordea_generic_soap_builder_test.rb +0 -427
- data/test/sepa/nordea_test_keys/nordea.crt +0 -27
- data/test/sepa/payload_test.rb +0 -297
- data/test/sepa/payment_test.rb +0 -198
- data/test/sepa/response_test.rb +0 -269
- data/test/sepa/transaction_test.rb +0 -362
- data/test/sepa/user_file_type_test.rb +0 -21
- data/test/sepa/xml_parser_test.rb +0 -73
@@ -0,0 +1,132 @@
|
|
1
|
+
module Sepa
|
2
|
+
module DanskeSoapRequest
|
3
|
+
|
4
|
+
def find_correct_build
|
5
|
+
case @command
|
6
|
+
when :create_certificate
|
7
|
+
build_certificate_request
|
8
|
+
when :upload_file, :download_file, :get_user_info, :download_file_list
|
9
|
+
build_danske_generic_request
|
10
|
+
when :get_bank_certificate
|
11
|
+
build_get_bank_certificate_request
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def encrypt_application_request
|
16
|
+
encryption_cert = OpenSSL::X509::Certificate.new(@enc_cert)
|
17
|
+
encryption_public_key = encryption_cert.public_key
|
18
|
+
encryption_cert = format_cert(encryption_cert)
|
19
|
+
encrypted_ar, key = encrypt_ar
|
20
|
+
encrypted_key = encrypt_key(key, encryption_public_key)
|
21
|
+
build_encrypted_ar(encryption_cert, encrypted_key, encrypted_ar)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Encrypts a given symmetric encryption key with a public key and returns it in base64 encoded
|
25
|
+
# format
|
26
|
+
def encrypt_key(key, public_key)
|
27
|
+
encrypted_key = public_key.public_encrypt(key)
|
28
|
+
Base64.encode64(encrypted_key)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Encrypts the application request and returns it in base64 encoded format.
|
32
|
+
# Also returns the key needed to decrypt it
|
33
|
+
def encrypt_ar
|
34
|
+
cipher = OpenSSL::Cipher.new('DES-EDE3-CBC').encrypt
|
35
|
+
|
36
|
+
key = cipher.random_key
|
37
|
+
iv = cipher.random_iv
|
38
|
+
|
39
|
+
encrypted_data = cipher.update(@ar.to_xml)
|
40
|
+
encrypted_data << cipher.final
|
41
|
+
encrypted_data = iv + encrypted_data
|
42
|
+
encrypted_data = Base64.encode64(encrypted_data)
|
43
|
+
|
44
|
+
return encrypted_data, key
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_encrypted_ar(cert, encrypted_data, encrypted_key)
|
48
|
+
ar = Nokogiri::XML File.open "#{AR_TEMPLATE_PATH}/encrypted_request.xml"
|
49
|
+
set_node(ar, 'dsig|X509Certificate', cert)
|
50
|
+
set_node(ar, 'dsig|KeyInfo xenc|CipherValue', encrypted_data)
|
51
|
+
set_node(ar, 'xenc|EncryptedData > xenc|CipherData > xenc|CipherValue', encrypted_key)
|
52
|
+
ar
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_generic_request_contents
|
56
|
+
set_node(@template, 'bxd|SenderId', @customer_id)
|
57
|
+
set_node(@template, 'bxd|RequestId', request_id)
|
58
|
+
set_node(@template, 'bxd|Timestamp', iso_time)
|
59
|
+
set_node(@template, 'bxd|Language', @language)
|
60
|
+
set_node(@template, 'bxd|UserAgent', "Sepa Transfer Library version " + VERSION)
|
61
|
+
set_node(@template, 'bxd|ReceiverId', @target_id)
|
62
|
+
end
|
63
|
+
|
64
|
+
def set_create_cert_contents
|
65
|
+
set_node(@template, 'pkif|SenderId', @customer_id)
|
66
|
+
set_node(@template, 'pkif|CustomerId', @customer_id)
|
67
|
+
set_node(@template, 'pkif|RequestId', request_id)
|
68
|
+
set_node(@template, 'pkif|Timestamp', iso_time)
|
69
|
+
set_node(@template, 'pkif|InterfaceVersion', 1)
|
70
|
+
set_node(@template, 'pkif|Environment', @environment)
|
71
|
+
end
|
72
|
+
|
73
|
+
def set_bank_certificate_contents
|
74
|
+
set_node(@template, 'pkif|SenderId', @customer_id)
|
75
|
+
set_node(@template, 'pkif|CustomerId', @customer_id)
|
76
|
+
set_node(@template, 'pkif|RequestId', request_id)
|
77
|
+
set_node(@template, 'pkif|Timestamp', iso_time)
|
78
|
+
set_node(@template, 'pkif|InterfaceVersion', 1)
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_danske_generic_request
|
82
|
+
set_generic_request_contents
|
83
|
+
encrypted_request = encrypt_application_request
|
84
|
+
add_encrypted_generic_request_to_soap(encrypted_request)
|
85
|
+
|
86
|
+
process_header
|
87
|
+
add_body_to_header
|
88
|
+
end
|
89
|
+
|
90
|
+
def build_certificate_request
|
91
|
+
set_create_cert_contents
|
92
|
+
encrypted_request = encrypt_application_request
|
93
|
+
add_encrypted_request_to_soap(encrypted_request)
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_get_bank_certificate_request
|
97
|
+
set_bank_certificate_contents
|
98
|
+
add_bank_certificate_body_to_soap
|
99
|
+
end
|
100
|
+
|
101
|
+
def add_encrypted_request_to_soap(encrypted_request)
|
102
|
+
encrypted_request = Nokogiri::XML(encrypted_request.to_xml)
|
103
|
+
encrypted_request = encrypted_request.root
|
104
|
+
@template.at_css('pkif|CreateCertificateIn').add_child(encrypted_request)
|
105
|
+
|
106
|
+
@template
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_encrypted_generic_request_to_soap(encrypted_request)
|
110
|
+
encrypted_request = Nokogiri::XML(encrypted_request.to_xml)
|
111
|
+
encrypted_request = encrypted_request.root
|
112
|
+
encrypted_request = Base64.encode64(encrypted_request.to_xml)
|
113
|
+
@template.at_css('bxd|ApplicationRequest').add_child(encrypted_request)
|
114
|
+
|
115
|
+
@template
|
116
|
+
end
|
117
|
+
|
118
|
+
def add_bank_certificate_body_to_soap
|
119
|
+
ar = @ar.to_nokogiri
|
120
|
+
|
121
|
+
ar = ar.at_css('elem|GetBankCertificateRequest')
|
122
|
+
@template.at_css('pkif|GetBankCertificateIn').add_child(ar)
|
123
|
+
|
124
|
+
@template
|
125
|
+
end
|
126
|
+
|
127
|
+
def request_id
|
128
|
+
SecureRandom.hex(5)
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sepa
|
2
|
+
class NordeaResponse < Response
|
3
|
+
|
4
|
+
def initialize(response, command: nil)
|
5
|
+
super
|
6
|
+
|
7
|
+
if @command == :get_certificate
|
8
|
+
@application_response = extract_application_response('http://bxd.fi/CertificateService')
|
9
|
+
@content = extract_own_cert
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def extract_own_cert
|
14
|
+
node = Nokogiri::XML(@application_response)
|
15
|
+
.at('xmlns|Certificate > xmlns|Certificate', xmlns: 'http://filetransfer.nordea.com/xmldata/')
|
16
|
+
|
17
|
+
Base64.encode64(OpenSSL::X509::Certificate.new(process_cert_value(node.content)).to_s) if node
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Sepa
|
2
|
+
module NordeaSoapRequest
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def find_correct_build
|
7
|
+
case @command
|
8
|
+
when :get_certificate
|
9
|
+
build_certificate_request
|
10
|
+
when :get_user_info, :download_file_list, :download_file, :upload_file
|
11
|
+
build_common_request
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Builds : Get Certificate
|
16
|
+
def build_certificate_request
|
17
|
+
set_body_contents
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_body_contents
|
21
|
+
set_node(@template, 'cer|ApplicationRequest', @ar.to_base64)
|
22
|
+
set_node(@template, 'cer|SenderId', @customer_id)
|
23
|
+
set_node(@template, 'cer|RequestId', request_id)
|
24
|
+
set_node(@template, 'cer|Timestamp', iso_time)
|
25
|
+
|
26
|
+
@template
|
27
|
+
end
|
28
|
+
|
29
|
+
# Builds : Get User Info, Download File, Download File List, Upload File
|
30
|
+
def build_common_request
|
31
|
+
common_set_body_contents
|
32
|
+
process_header
|
33
|
+
add_body_to_header
|
34
|
+
end
|
35
|
+
|
36
|
+
def common_set_body_contents
|
37
|
+
set_node(@template, 'bxd|ApplicationRequest', @ar.to_base64)
|
38
|
+
set_node(@template, 'bxd|SenderId', @customer_id)
|
39
|
+
set_node(@template, 'bxd|RequestId', request_id)
|
40
|
+
set_node(@template, 'bxd|Timestamp', iso_time)
|
41
|
+
set_node(@template, 'bxd|Language', @language)
|
42
|
+
set_node(@template, 'bxd|UserAgent', "Sepa Transfer Library version #{VERSION}")
|
43
|
+
set_node(@template, 'bxd|ReceiverId', @target_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
def request_id
|
47
|
+
SecureRandom.hex(17)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
data/lib/sepa/client.rb
CHANGED
@@ -1,79 +1,91 @@
|
|
1
1
|
module Sepa
|
2
2
|
class Client
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
3
|
+
include ActiveModel::Validations
|
4
|
+
include Utilities
|
5
|
+
include ErrorMessages
|
6
|
+
include AttributeChecks
|
7
|
+
|
8
|
+
attr_accessor :bank, :cert, :command, :content, :customer_id, :enc_cert,
|
9
|
+
:encryption_cert_pkcs10, :environment, :file_reference,
|
10
|
+
:file_type, :key_generator_type, :language, :pin, :private_key,
|
11
|
+
:signing_cert_pkcs10, :status, :target_id, :csr, :service, :bank_root_cert_serial
|
12
|
+
|
13
|
+
BANKS = [:nordea, :danske]
|
14
|
+
LANGUAGES = ['FI', 'SE', 'EN']
|
15
|
+
|
16
|
+
validates :bank, inclusion: { in: BANKS }
|
17
|
+
validates :language, inclusion: { in: LANGUAGES }, allow_nil: true
|
18
|
+
|
19
|
+
validate :check_status
|
20
|
+
validate :check_customer_id
|
21
|
+
validate :check_file_type
|
22
|
+
validate :check_environment
|
23
|
+
validate :check_target_id
|
24
|
+
validate :check_content
|
25
|
+
validate :check_pin
|
26
|
+
validate :check_command
|
27
|
+
validate :check_wsdl
|
28
|
+
validate :check_keys
|
29
|
+
validate :check_enc_cert
|
30
|
+
validate :check_encryption_cert_request
|
31
|
+
validate :check_signing_cert
|
32
|
+
validate :check_bank_root_cert_serial
|
33
|
+
validate :check_file_reference
|
34
|
+
|
35
|
+
def initialize(hash = {})
|
36
|
+
self.attributes hash
|
37
|
+
self.environment ||= 'PRODUCTION'
|
16
38
|
end
|
17
39
|
|
18
|
-
|
19
|
-
|
20
|
-
def send
|
21
|
-
@client.call(@command, xml: @soap)
|
40
|
+
def bank=(value)
|
41
|
+
@bank = value.to_sym
|
22
42
|
end
|
23
43
|
|
24
|
-
|
44
|
+
def command=(value)
|
45
|
+
@command = value.to_sym
|
46
|
+
end
|
25
47
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
"Acceptable values are nordea OR danske."
|
30
|
-
end
|
48
|
+
def attributes(hash)
|
49
|
+
hash.each do |name, value|
|
50
|
+
send("#{name}=", value)
|
31
51
|
end
|
52
|
+
end
|
32
53
|
|
33
|
-
|
34
|
-
|
35
|
-
case bank
|
36
|
-
when :nordea
|
37
|
-
if command == :get_certificate
|
38
|
-
path = "#{wsdlpath}/wsdl_nordea_cert.xml"
|
39
|
-
else
|
40
|
-
path = "#{wsdlpath}/wsdl_nordea.xml"
|
41
|
-
end
|
42
|
-
when :danske
|
43
|
-
if command == :get_bank_certificate
|
44
|
-
path = "#{wsdlpath}/wsdl_danske_cert.xml"
|
45
|
-
else
|
46
|
-
path = "#{wsdlpath}/wsdl_danske.xml"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
check_wsdl(path)
|
50
|
-
path
|
51
|
-
end
|
54
|
+
def send_request
|
55
|
+
raise ArgumentError, errors.messages unless valid?
|
52
56
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
+
soap = SoapBuilder.new(create_hash).to_xml
|
58
|
+
client = Savon.client(wsdl: wsdl)
|
59
|
+
response = client.call(command, xml: soap).doc
|
60
|
+
|
61
|
+
case bank
|
62
|
+
when :nordea
|
63
|
+
NordeaResponse.new response, command: command
|
64
|
+
when :danske
|
65
|
+
DanskeResponse.new response, command: command
|
57
66
|
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def create_hash
|
72
|
+
initialize_private_key
|
73
|
+
iv = {}
|
58
74
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
75
|
+
# Create hash of all instance variables
|
76
|
+
instance_variables.map do |name|
|
77
|
+
key = name[1..-1].to_sym
|
78
|
+
value = instance_variable_get(name)
|
63
79
|
|
64
|
-
|
65
|
-
wsdl_file = File.read(wsdl)
|
66
|
-
rescue
|
67
|
-
fail ArgumentError, "You didn't provide a wsdl file or the path is " \
|
68
|
-
"invalid"
|
80
|
+
iv[key] = value
|
69
81
|
end
|
70
82
|
|
71
|
-
|
83
|
+
iv
|
84
|
+
end
|
72
85
|
|
73
|
-
|
74
|
-
|
75
|
-
"against the wsdl schema and thus was rejected."
|
76
|
-
end
|
86
|
+
def initialize_private_key
|
87
|
+
@private_key = OpenSSL::PKey::RSA.new(@private_key) if @private_key
|
77
88
|
end
|
89
|
+
|
78
90
|
end
|
79
91
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Sepa
|
2
|
+
module ErrorMessages
|
3
|
+
CUSTOMER_ID_ERROR_MESSAGE = 'Customer Id needs to be present and needs to have a length of less than 17 characters'
|
4
|
+
ENVIRONMENT_ERROR_MESSAGE = 'Environment needs to be either PRODUCTION, TEST or customertest'
|
5
|
+
TARGET_ID_ERROR_MESSAGE = 'Target Id needs to be present and under 80 characters'
|
6
|
+
FILE_TYPE_ERROR_MESSAGE = 'File type needs to be present and under 35 characters'
|
7
|
+
CONTENT_ERROR_MESSAGE = 'Content needs to be present for this command'
|
8
|
+
SIGNING_CERT_REQUEST_ERROR_MESSAGE = 'Invalid signing certificate request'
|
9
|
+
ENCRYPTION_CERT_REQUEST_ERROR_MESSAGE = 'Invalid encryption certificate request'
|
10
|
+
PIN_ERROR_MESSAGE = 'Pin needs to be present for this command and cannot be more than 10 characters'
|
11
|
+
ENCRYPTION_CERT_ERROR_MESSAGE = 'Invalid encryption certificate'
|
12
|
+
STATUS_ERROR_MESSAGE = 'Status is required for this command and must be either NEW, DOWNLOADED or ALL'
|
13
|
+
FILE_REFERENCE_ERROR_MESSAGE = 'File reference is required for this command and must be under 33 characters'
|
14
|
+
end
|
15
|
+
end
|
data/lib/sepa/response.rb
CHANGED
@@ -1,45 +1,28 @@
|
|
1
1
|
module Sepa
|
2
2
|
class Response
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
if !@response.respond_to?(:canonicalize)
|
7
|
-
fail ArgumentError,
|
8
|
-
"The response you provided is not a valid Nokogiri::XML file."
|
9
|
-
elsif !valid_against_schema?(@response)
|
10
|
-
fail ArgumentError,
|
11
|
-
"The response you provided doesn't validate against soap schema."
|
12
|
-
end
|
13
|
-
end
|
3
|
+
include ActiveModel::Validations
|
4
|
+
include Utilities
|
14
5
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
begin
|
27
|
-
cert = OpenSSL::X509::Certificate.new(cert)
|
28
|
-
rescue => e
|
29
|
-
fail OpenSSL::X509::CertificateError,
|
30
|
-
"The certificate embedded to the soap response could not be process" \
|
31
|
-
"ed. It's most likely corrupted. OpenSSL had this to say: #{e}."
|
32
|
-
end
|
33
|
-
end
|
6
|
+
attr_reader :soap, :application_response, :certificate, :content
|
7
|
+
|
8
|
+
validates :soap, presence: true
|
9
|
+
validate :validate_document_format
|
10
|
+
validate :document_must_validate_against_schema
|
11
|
+
|
12
|
+
GENERIC_COMMANDS = [:get_user_info, :download_file_list, :download_file, :upload_file]
|
13
|
+
|
14
|
+
def initialize(response, command: nil)
|
15
|
+
@soap = response
|
16
|
+
@command = command
|
34
17
|
|
35
|
-
|
36
|
-
|
37
|
-
if
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
18
|
+
# Check if command is one of the generic commands which should behave the same way across
|
19
|
+
# different banks
|
20
|
+
if GENERIC_COMMANDS.include? command
|
21
|
+
xsd = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
|
22
|
+
|
23
|
+
@application_response = extract_application_response('http://model.bxd.fi')
|
24
|
+
@certificate = extract_cert(soap, 'BinarySecurityToken', xsd)
|
25
|
+
@content = extract_content
|
43
26
|
end
|
44
27
|
end
|
45
28
|
|
@@ -47,42 +30,39 @@ module Sepa
|
|
47
30
|
# Takes an optional verbose parameter to show which digests didn't match
|
48
31
|
# i.e. verbose: true
|
49
32
|
def hashes_match?(options = {})
|
50
|
-
digests = find_digest_values
|
51
|
-
nodes = find_nodes_to_verify(
|
33
|
+
digests = find_digest_values
|
34
|
+
nodes = find_nodes_to_verify(soap, digests)
|
52
35
|
|
53
36
|
verified_digests = digests.select do |uri, digest|
|
54
37
|
uri = uri.sub(/^#/, '')
|
55
38
|
digest == nodes[uri]
|
56
39
|
end
|
57
40
|
|
58
|
-
if digests == verified_digests
|
59
|
-
true
|
60
|
-
else
|
61
|
-
unverified_digests = digests.select do |uri, digest|
|
62
|
-
uri = uri.sub(/^#/, '')
|
63
|
-
digest != nodes[uri]
|
64
|
-
end
|
41
|
+
return true if digests == verified_digests
|
65
42
|
|
66
|
-
|
67
|
-
|
68
|
-
|
43
|
+
unverified_digests = digests.select do |uri, digest|
|
44
|
+
uri = uri.sub(/^#/, '')
|
45
|
+
digest != nodes[uri]
|
46
|
+
end
|
69
47
|
|
70
|
-
|
48
|
+
if options[:verbose]
|
49
|
+
puts "These digests failed to verify: #{unverified_digests}."
|
71
50
|
end
|
51
|
+
|
52
|
+
false
|
72
53
|
end
|
73
54
|
|
74
55
|
# Verifies the signature by extracting the public key from the certificate
|
75
56
|
# embedded in the soap header and verifying the signature value with that.
|
76
57
|
def signature_is_valid?
|
77
|
-
node =
|
78
|
-
'xmlns' => 'http://www.w3.org/2000/09/xmldsig#')
|
58
|
+
node = soap.at_css('xmlns|SignedInfo', 'xmlns' => 'http://www.w3.org/2000/09/xmldsig#')
|
79
59
|
|
80
60
|
node = node.canonicalize(
|
81
|
-
mode=Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0,
|
82
|
-
inclusive_namespaces=nil,with_comments=false
|
61
|
+
mode = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0,
|
62
|
+
inclusive_namespaces = nil, with_comments = false
|
83
63
|
)
|
84
64
|
|
85
|
-
signature =
|
65
|
+
signature = soap.at_css(
|
86
66
|
'xmlns|SignatureValue',
|
87
67
|
'xmlns' => 'http://www.w3.org/2000/09/xmldsig#'
|
88
68
|
).content
|
@@ -95,18 +75,28 @@ module Sepa
|
|
95
75
|
# Gets the application response from the response as an Nokogiri::XML
|
96
76
|
# document
|
97
77
|
def application_response
|
98
|
-
ar =
|
78
|
+
ar = soap.at_css('mod|ApplicationResponse').content
|
99
79
|
ar = Base64.decode64(ar)
|
100
80
|
Nokogiri::XML(ar)
|
101
81
|
end
|
102
82
|
|
83
|
+
def file_references
|
84
|
+
return unless @command == :download_file_list
|
85
|
+
|
86
|
+
@file_references ||= begin
|
87
|
+
content = Nokogiri::XML @content
|
88
|
+
descriptors = content.css('FileDescriptor')
|
89
|
+
descriptors.map { |descriptor| descriptor.at('FileReference').content }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
103
93
|
private
|
104
94
|
|
105
95
|
# Finds all reference nodes with digest values in the document and returns
|
106
96
|
# a hash with uri as the key and digest as the value.
|
107
|
-
def find_digest_values
|
97
|
+
def find_digest_values
|
108
98
|
references = {}
|
109
|
-
reference_nodes =
|
99
|
+
reference_nodes = soap.css(
|
110
100
|
'xmlns|Reference',
|
111
101
|
'xmlns' => 'http://www.w3.org/2000/09/xmldsig#'
|
112
102
|
)
|
@@ -128,12 +118,14 @@ module Sepa
|
|
128
118
|
# references hash.
|
129
119
|
def find_nodes_to_verify(doc, references)
|
130
120
|
nodes = {}
|
121
|
+
|
131
122
|
references.each do |uri, digest_value|
|
132
123
|
uri = uri.sub(/^#/, '')
|
124
|
+
wsu = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'
|
125
|
+
|
133
126
|
node = doc.at_css(
|
134
|
-
"[wsu|Id='
|
135
|
-
'wsu' =>
|
136
|
-
'-wssecurity-utility-1.0.xsd'
|
127
|
+
"[wsu|Id='#{uri}']",
|
128
|
+
'wsu' => wsu
|
137
129
|
)
|
138
130
|
|
139
131
|
nodes[uri] = calculate_digest(node)
|
@@ -142,36 +134,47 @@ module Sepa
|
|
142
134
|
nodes
|
143
135
|
end
|
144
136
|
|
145
|
-
def
|
146
|
-
|
137
|
+
def validate_document_format
|
138
|
+
unless soap.respond_to?(:canonicalize)
|
139
|
+
errors.add(:base, 'Document must be a Nokogiri XML file')
|
140
|
+
end
|
141
|
+
end
|
147
142
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
)
|
143
|
+
def document_must_validate_against_schema
|
144
|
+
check_validity_against_schema(soap, 'soap.xsd')
|
145
|
+
end
|
152
146
|
|
153
|
-
|
147
|
+
def extract_content
|
148
|
+
xml = Nokogiri::XML(@application_response)
|
149
|
+
xmlns = 'http://bxd.fi/xmldata/'
|
150
|
+
|
151
|
+
case @command
|
152
|
+
when :download_file
|
153
|
+
content_node = xml.at('xmlns|Content', xmlns: xmlns)
|
154
|
+
content_node.content if content_node
|
155
|
+
when :download_file_list
|
156
|
+
content_node = xml.remove_namespaces!.at('FileDescriptors')
|
157
|
+
content_node.to_xml if content_node
|
158
|
+
when :get_user_info
|
159
|
+
canonicalized_node(xml, xmlns, 'UserFileTypes')
|
160
|
+
when :upload_file
|
161
|
+
signature_node = xml.at('xmlns|Signature', xmlns: 'http://www.w3.org/2000/09/xmldsig#')
|
162
|
+
if signature_node
|
163
|
+
signature_node.remove
|
164
|
+
xml.canonicalize
|
165
|
+
end
|
166
|
+
end
|
154
167
|
end
|
155
168
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
169
|
+
def extract_application_response(namespace)
|
170
|
+
if soap.respond_to? :at_css
|
171
|
+
ar_node = soap.at_css('xmlns|ApplicationResponse', xmlns: namespace)
|
172
|
+
end
|
160
173
|
|
161
|
-
|
162
|
-
|
163
|
-
xsd.valid?(doc)
|
174
|
+
if ar_node
|
175
|
+
Base64.decode64(ar_node.content)
|
164
176
|
end
|
165
177
|
end
|
166
178
|
|
167
|
-
# Takes the certificate from the response, adds begin and end
|
168
|
-
# certificate texts and splits it into multiple lines so that OpenSSL
|
169
|
-
# can read it.
|
170
|
-
def process_cert_value(cert_value)
|
171
|
-
cert = "-----BEGIN CERTIFICATE-----\n"
|
172
|
-
cert += cert_value.to_s.gsub(/\s+/, "").scan(/.{1,64}/).join("\n")
|
173
|
-
cert += "\n"
|
174
|
-
cert += "-----END CERTIFICATE-----"
|
175
|
-
end
|
176
179
|
end
|
177
180
|
end
|