sepafm 1.1.8 → 1.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -6
  3. data/Rakefile +1 -1
  4. data/lib/sepa/application_request.rb +12 -18
  5. data/lib/sepa/application_response.rb +0 -2
  6. data/lib/sepa/attribute_checks.rb +33 -28
  7. data/lib/sepa/banks/danske/danske_response.rb +9 -25
  8. data/lib/sepa/banks/danske/soap_danske.rb +1 -1
  9. data/lib/sepa/banks/nordea/nordea_response.rb +2 -16
  10. data/lib/sepa/banks/op/op_response.rb +5 -23
  11. data/lib/sepa/banks/samlink/samlink_response.rb +35 -0
  12. data/lib/sepa/banks/samlink/soap_samlink.rb +14 -0
  13. data/lib/sepa/certificates/samlink_certificate.pem +29 -0
  14. data/lib/sepa/certificates/samlink_root_certificate.pem +32 -0
  15. data/lib/sepa/client.rb +35 -8
  16. data/lib/sepa/error_messages.rb +16 -18
  17. data/lib/sepa/response.rb +7 -11
  18. data/lib/sepa/soap_builder.rb +7 -17
  19. data/lib/sepa/utilities.rb +4 -5
  20. data/lib/sepa/version.rb +1 -1
  21. data/lib/sepa/wsdl/wsdl_samlink_cert_production.xml +82 -0
  22. data/lib/sepa/wsdl/wsdl_samlink_cert_test.xml +82 -0
  23. data/lib/sepa/wsdl/wsdl_samlink_production.xml +160 -0
  24. data/lib/sepa/wsdl/wsdl_samlink_test.xml +160 -0
  25. data/lib/sepa/xml_schemas/samlink/CertApplicationRequest.xsd +105 -0
  26. data/lib/sepa/xml_schemas/samlink/CertApplicationResponse.xsd +88 -0
  27. data/lib/sepa/xml_templates/application_request/download_file.xml +0 -1
  28. data/lib/sepa/xml_templates/application_request/download_file_list.xml +0 -1
  29. data/lib/sepa/xml_templates/application_request/samlink/get_certificate.xml +12 -0
  30. data/lib/sepa/xml_templates/application_request/samlink/renew_certificate.xml +29 -0
  31. data/lib/sepa/xml_templates/soap/samlink/get_certificate.xml +14 -0
  32. data/lib/sepa/xml_templates/soap/samlink/renew_certificate.xml +14 -0
  33. data/lib/sepafm.rb +43 -31
  34. data/readme.md +1 -0
  35. data/sepafm.gemspec +2 -2
  36. data/test/custom_assertions.rb +30 -28
  37. data/test/sepa/banks/danske/danske_cert_response_test.rb +13 -10
  38. data/test/sepa/banks/danske/danske_generic_soap_builder_test.rb +9 -31
  39. data/test/sepa/banks/danske/danske_get_bank_cert_test.rb +4 -5
  40. data/test/sepa/banks/danske/danske_response_test.rb +2 -3
  41. data/test/sepa/banks/danske/responses/create_cert_corrupted.xml +15 -0
  42. data/test/sepa/banks/nordea/nordea_application_request_test.rb +4 -6
  43. data/test/sepa/banks/nordea/nordea_application_response_test.rb +14 -15
  44. data/test/sepa/banks/nordea/nordea_cert_request_soap_builder_test.rb +1 -3
  45. data/test/sepa/banks/nordea/nordea_generic_soap_builder_test.rb +6 -16
  46. data/test/sepa/banks/nordea/nordea_response_test.rb +11 -11
  47. data/test/sepa/banks/op/op_cert_application_request_test.rb +1 -1
  48. data/test/sepa/banks/op/op_cert_request_soap_builder_test.rb +0 -1
  49. data/test/sepa/banks/op/op_response_test.rb +2 -2
  50. data/test/sepa/banks/samlink/responses/dfl.xml +21 -0
  51. data/test/sepa/banks/samlink/responses/gc_error_30.xml +21 -0
  52. data/test/sepa/banks/samlink/responses/rc.xml +21 -0
  53. data/test/sepa/banks/samlink/samlink_application_request_test.rb +36 -0
  54. data/test/sepa/banks/samlink/samlink_cert_application_request_test.rb +13 -0
  55. data/test/sepa/banks/samlink/samlink_cert_request_soap_builder_test.rb +13 -0
  56. data/test/sepa/banks/samlink/samlink_generic_soap_builder_test.rb +34 -0
  57. data/test/sepa/banks/samlink/samlink_renew_cert_application_request_test.rb +36 -0
  58. data/test/sepa/banks/samlink/samlink_renew_cert_request_soap_builder_test.rb +26 -0
  59. data/test/sepa/banks/samlink/samlink_response_test.rb +71 -0
  60. data/test/sepa/client_test.rb +32 -6
  61. data/test/sepa/fixtures.rb +169 -7
  62. data/test/sepa/sepa_test.rb +1 -1
  63. data/test/test_helper.rb +8 -7
  64. data/test_client/data/certs_example.rb +9 -9
  65. data/test_client/data/params_example.rb +18 -19
  66. data/test_client/test_client.rb +6 -0
  67. metadata +41 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4495553a8331dc839714be9d80f714bcdad98a00
4
- data.tar.gz: 60935bd6d5859e8e812943c40c64854671b365c4
3
+ metadata.gz: d7953437ff7a58f58b2f7df4b1e9541ceb409000
4
+ data.tar.gz: ac839e70c1644adb076586e5ac98fa78779fd8a3
5
5
  SHA512:
6
- metadata.gz: f8cad4589cb2968fa5ff25dbf14542584973549dd5d3836e404dea824bdfecaed3deceeaddd62d8f191734040910f6d810617d40fce84762931c7e24d73ceec7
7
- data.tar.gz: 726a6d88274b0f076f86ba1798c46649e304f6da1f5a23f6f8669eab07c5e96fef2a52b0257b47cc3755e4bf3e1771fa929b7ec69b38548dd5aedbbc68b2a45e
6
+ metadata.gz: 8c8b5f59d6bac6b85f71d893039f1ffe707c78258c1f65546c7b64c650f27bc7b389196ae62513b0c0e933c20ec488370b7aa220c5b3297375b602d07aada7f0
7
+ data.tar.gz: ed2acb7fa7b253c93cd36be5eca1750e40e709b43914baa876ee68b5bea5a242ac3d46b12672a4659301b554afe276ea374dc45e7a7667bd18cfe26d61f5f11e
data/.rubocop.yml CHANGED
@@ -5,7 +5,7 @@ Metrics/AbcSize:
5
5
  Max: 37
6
6
 
7
7
  Metrics/ClassLength:
8
- Max: 348
8
+ Max: 350
9
9
 
10
10
  Metrics/CyclomaticComplexity:
11
11
  Max: 8
@@ -17,7 +17,7 @@ Metrics/MethodLength:
17
17
  Max: 69
18
18
 
19
19
  Metrics/ModuleLength:
20
- Max: 146
20
+ Max: 150
21
21
 
22
22
  Style/Documentation:
23
23
  Exclude:
@@ -25,11 +25,13 @@ Style/Documentation:
25
25
  - 'test/**/*'
26
26
 
27
27
  Style/IndentationConsistency:
28
- SupportedStyles: rails
28
+ EnforcedStyle: rails
29
29
 
30
30
  Style/StringLiterals:
31
- SupportedStyles: single_quotes, double_quotes
32
- ConsistentQuotesInMultiline: true
31
+ Enabled: false
33
32
 
34
33
  Style/TrailingCommaInLiteral:
35
- SupportedStyles: comma
34
+ EnforcedStyleForMultiline: comma
35
+
36
+ Style/TrailingCommaInArguments:
37
+ EnforcedStyleForMultiline: comma
data/Rakefile CHANGED
@@ -12,4 +12,4 @@ task :console do
12
12
  sh "bundle exec irb -I lib -r sepafm.rb"
13
13
  end
14
14
 
15
- task :default => :test
15
+ task default: :test
@@ -74,7 +74,7 @@ module Sepa
74
74
  # @example Example input and output
75
75
  # :get_user_info --> GetUserInfo
76
76
  def pretty_command
77
- @command.to_s.split(/[\W_]/).map {|c| c.capitalize}.join
77
+ @command.to_s.split(/[\W_]/).map(&:capitalize).join
78
78
  end
79
79
 
80
80
  # Determines which content setting method to call depending on {#command}
@@ -86,8 +86,8 @@ module Sepa
86
86
 
87
87
  # Sets nodes' values for download file request
88
88
  def set_download_file_nodes
89
- add_target_id_after 'FileReferences'
90
- set_node("Status", @status)
89
+ add_node_after('FileReferences', 'TargetId', content: @target_id) if @bank == :nordea
90
+ add_node_after('Timestamp', 'Status', content: @status) if @status.present?
91
91
  add_node_to_root 'FileType', content: @file_type if @file_type.present?
92
92
  set_node("FileReference", @file_reference)
93
93
  end
@@ -110,20 +110,20 @@ module Sepa
110
110
  def set_upload_file_nodes
111
111
  set_node_b("Content", @content)
112
112
  set_node("FileType", @file_type)
113
- add_target_id_after 'Environment'
113
+ add_node_after('Environment', 'TargetId', content: @target_id) if @bank == :nordea
114
114
  end
115
115
 
116
116
  # Sets nodes' contents for download file list request
117
117
  def set_download_file_list_nodes
118
- add_target_id_after 'Environment'
119
- set_node("Status", @status)
118
+ add_node_after('Environment', 'TargetId', content: @target_id) if @bank == :nordea
119
+ add_node_after('Timestamp', 'Status', content: @status) if @status.present?
120
120
  add_node_to_root 'FileType', content: @file_type if @file_type.present?
121
121
  end
122
122
 
123
123
  # Sets nodes' contents for Nordea's and OP's get certificate request
124
124
  def set_get_certificate_nodes
125
125
  set_node "Service", "MATU" if @bank == :op
126
- set_node "TransferKey", @pin if @bank == :op
126
+ set_node "TransferKey", @pin if [:op, :samlink].include?(@bank)
127
127
  set_node "HMAC", hmac(@pin, csr_to_binary(@signing_csr)) if @bank == :nordea
128
128
  set_node "Content", format_cert_request(@signing_csr)
129
129
  end
@@ -131,7 +131,7 @@ module Sepa
131
131
  # Sets nodes' contents for renew certificate request
132
132
  def set_renew_certificate_nodes
133
133
  case @bank
134
- when :nordea, :op
134
+ when :nordea, :op, :samlink
135
135
  set_node "Service", "service" if @bank == :nordea
136
136
  set_node "Content", format_cert_request(@signing_csr)
137
137
  when :danske
@@ -255,16 +255,10 @@ module Sepa
255
255
  add_value_to_signature('X509Certificate', format_cert(@own_signing_certificate))
256
256
  end
257
257
 
258
- # Adds target id to the application request after a specific node because the schema defines a
259
- # sequence. Target id is only added if {#bank} is `:nordea`
260
- #
261
- # @param node [String] the name of the node after which the target id node will be added
262
- def add_target_id_after(node)
263
- return unless @bank == :nordea
264
-
265
- target_id = Nokogiri::XML::Node.new 'TargetId', @application_request
266
- target_id.content = @target_id
267
- @application_request.at(node).add_next_sibling target_id
258
+ def add_node_after(node, new_node, content:)
259
+ new_node = Nokogiri::XML::Node.new(new_node, @application_request)
260
+ new_node.content = content
261
+ @application_request.at(node).add_next_sibling(new_node)
268
262
  end
269
263
 
270
264
  def canonicalization_mode
@@ -1,5 +1,4 @@
1
1
  module Sepa
2
-
3
2
  # Contains functionality for the application response embedded in {Response}
4
3
  # @todo Use functionality from this class more when validating response
5
4
  class ApplicationResponse
@@ -96,6 +95,5 @@ module Sepa
96
95
  def response_must_validate_against_schema
97
96
  check_validity_against_schema(doc, 'application_response.xsd')
98
97
  end
99
-
100
98
  end
101
99
  end
@@ -13,7 +13,7 @@ module Sepa
13
13
  [
14
14
  STANDARD_COMMANDS,
15
15
  :get_certificate,
16
- :renew_certificate
16
+ :renew_certificate,
17
17
  ].flatten
18
18
  when :danske
19
19
  [
@@ -28,6 +28,12 @@ module Sepa
28
28
  :get_certificate,
29
29
  :get_service_certificates,
30
30
  ].flatten
31
+ when :samlink
32
+ [
33
+ STANDARD_COMMANDS - [:get_user_info],
34
+ :get_certificate,
35
+ :renew_certificate,
36
+ ].flatten
31
37
  else
32
38
  []
33
39
  end
@@ -53,11 +59,9 @@ module Sepa
53
59
  errors.add(:signing_private_key, "Invalid signing private key")
54
60
  end
55
61
 
56
- begin
57
- x509_certificate own_signing_certificate
58
- rescue
59
- errors.add(:own_signing_certificate, "Invalid signing certificate")
60
- end
62
+ x509_certificate own_signing_certificate
63
+ rescue
64
+ errors.add(:own_signing_certificate, "Invalid signing certificate")
61
65
  end
62
66
 
63
67
  # Checks that signing certificate signing request can be initialized properly.
@@ -82,8 +86,7 @@ module Sepa
82
86
  if file_type.present?
83
87
  valid = file_type.size < 35
84
88
  else
85
- return if bank == :op && %i(download_file
86
- download_file_list).include?(command)
89
+ return if bank == :op && %i(download_file download_file_list).include?(command)
87
90
 
88
91
  valid = !(%i(
89
92
  download_file
@@ -97,17 +100,21 @@ module Sepa
97
100
 
98
101
  # Checks that {Client#target_id} is valid.
99
102
  def check_target_id
100
- return if %i(
101
- create_certificate
102
- get_bank_certificate
103
- get_certificate
104
- renew_certificate
105
- get_user_info
106
- ).include?(command) ||
107
- %i(
108
- danske
109
- op
110
- ).include?(bank)
103
+ exclude_commands = [
104
+ :create_certificate,
105
+ :get_bank_certificate,
106
+ :get_certificate,
107
+ :get_user_info,
108
+ :renew_certificate,
109
+ ]
110
+
111
+ exclude_banks = [
112
+ :danske,
113
+ :op,
114
+ :samlink,
115
+ ]
116
+
117
+ return if exclude_commands.include?(command) || exclude_banks.include?(bank)
111
118
 
112
119
  check_presence_and_length(:target_id, 80, TARGET_ID_ERROR_MESSAGE)
113
120
  end
@@ -122,7 +129,7 @@ module Sepa
122
129
  check &&= send(attribute)
123
130
  check &&= send(attribute).respond_to? :size
124
131
  check &&= send(attribute).size < length
125
- check &&= send(attribute).size > 0
132
+ check &&= !send(attribute).empty?
126
133
 
127
134
  errors.add(attribute, error_message) unless check
128
135
  end
@@ -135,7 +142,7 @@ module Sepa
135
142
  check = true
136
143
  check &&= content
137
144
  check &&= content.respond_to? :length
138
- check &&= content.length > 0
145
+ check &&= !content.empty?
139
146
 
140
147
  errors.add(:content, CONTENT_ERROR_MESSAGE) unless check
141
148
  end
@@ -151,10 +158,9 @@ module Sepa
151
158
  # {Client#command} is `:get_bank_certificate`.
152
159
  def check_environment
153
160
  return if command == :get_bank_certificate
161
+ return if Client::ENVIRONMENTS.include?(environment)
154
162
 
155
- unless Client::ENVIRONMENTS.include? environment
156
- errors.add(:environment, ENVIRONMENT_ERROR_MESSAGE)
157
- end
163
+ errors.add(:environment, ENVIRONMENT_ERROR_MESSAGE)
158
164
  end
159
165
 
160
166
  # Checks that {Client#customer_id} is valid
@@ -182,11 +188,11 @@ module Sepa
182
188
 
183
189
  # Checks that {Client#status} is included in {Client::STATUSES}.
184
190
  def check_status
191
+ return if bank == :samlink && command != :download_file_list
185
192
  return unless [:download_file_list, :download_file].include? command
193
+ return if status && Client::STATUSES.include?(status)
186
194
 
187
- unless status && Client::STATUSES.include?(status)
188
- errors.add :status, STATUS_ERROR_MESSAGE
189
- end
195
+ errors.add :status, STATUS_ERROR_MESSAGE
190
196
  end
191
197
 
192
198
  # Checks presence and length of {Client#file_reference} if {Client#command} is `:download_file`
@@ -208,6 +214,5 @@ module Sepa
208
214
  rescue
209
215
  errors.add :encryption_private_key, ENCRYPTION_PRIVATE_KEY_ERROR_MESSAGE
210
216
  end
211
-
212
217
  end
213
218
  end
@@ -2,6 +2,8 @@ module Sepa
2
2
  # Handles Danske Bank specific {Response} functionality. Mainly decryption and certificate
3
3
  # specific stuff.
4
4
  class DanskeResponse < Response
5
+ CERTIFICATE_COMMANDS = [:get_bank_certificate, :create_certificate, :renew_certificate].freeze
6
+
5
7
  validate :valid_get_bank_certificate_response
6
8
  validate :can_be_decrypted_with_given_key
7
9
 
@@ -84,41 +86,23 @@ module Sepa
84
86
  # @return [OpenSSL::X509::Certificate]
85
87
  # @raise [OpenSSL::X509::CertificateError] if certificate cannot be processed
86
88
  def certificate
87
- return super unless [:get_bank_certificate, :create_certificate, :renew_certificate].include? @command
89
+ return super unless CERTIFICATE_COMMANDS.include? @command
88
90
 
89
91
  @certificate ||= extract_cert(doc, 'X509Certificate', DSIG)
90
92
  end
91
93
 
92
- # Extract response code from the response. Overrides super method when {#command} is
93
- # `:get_bank_certificate`, `:create_certificate` or `:renew_certificate` because response code node is named
94
- # differently in those responses.
95
- #
96
- # @return [String] if response code is found
97
- # @return [nil] if response code cannot be found
98
94
  # @see Response#response_code
99
95
  def response_code
100
- return super unless [:get_bank_certificate, :create_certificate, :renew_certificate].include? @command
101
-
102
- node = doc.at('xmlns|ReturnCode', xmlns: DANSKE_PKI)
103
- node = doc.at('xmlns|ReturnCode', xmlns: DANSKE_PKIF) unless node
96
+ return super unless CERTIFICATE_COMMANDS.include? @command
104
97
 
105
- node.content if node
98
+ super(namespace: DANSKE_PKI, node_name: 'ReturnCode') || super(namespace: DANSKE_PKIF, node_name: 'ReturnCode')
106
99
  end
107
100
 
108
- # Extract response text from the response. Overrides super method when {#command} is
109
- # `:get_bank_certificate`, `:create_certificate` or `:renew_certificate` because response text node is named
110
- # differently in those responses.
111
- #
112
- # @return [String] if response text is found
113
- # @return [nil] if response text cannot be found
114
101
  # @see Response#response_text
115
102
  def response_text
116
- return super unless [:get_bank_certificate, :create_certificate, :renew_certificate].include? @command
117
-
118
- node = doc.at('xmlns|ReturnText', xmlns: DANSKE_PKI)
119
- node = doc.at('xmlns|ReturnText', xmlns: DANSKE_PKIF) unless node
103
+ return super unless CERTIFICATE_COMMANDS.include? @command
120
104
 
121
- node.content if node
105
+ super(namespace: DANSKE_PKI, node_name: 'ReturnText') || super(namespace: DANSKE_PKIF, node_name: 'ReturnText')
122
106
  end
123
107
 
124
108
  # Checks whether certificate embedded in the response has been signed with the bank's root
@@ -144,7 +128,7 @@ module Sepa
144
128
  # @return [Nokogiri::XML::Node] node with signature removed from its document since signature
145
129
  # has to be removed for canonicalization and hash calculation
146
130
  def find_node_by_uri(uri)
147
- return super unless [:get_bank_certificate, :create_certificate, :renew_certificate].include? @command
131
+ return super unless CERTIFICATE_COMMANDS.include? @command
148
132
 
149
133
  doc_without_signature = doc.dup
150
134
  doc_without_signature.at('xmlns|Signature', xmlns: DSIG).remove
@@ -203,7 +187,7 @@ module Sepa
203
187
  # Validates that the encrypted key in the response can be decrypted with the private key given
204
188
  # to the response in the parameters. Response is invalid if this cannot be done.
205
189
  def can_be_decrypted_with_given_key
206
- return if [:get_bank_certificate, :create_certificate, :renew_certificate].include? @command
190
+ return if CERTIFICATE_COMMANDS.include? @command
207
191
  return unless encrypted_application_response.css('CipherValue', 'xmlns' => XMLENC)[0]
208
192
  return if decrypt_embedded_key
209
193
 
@@ -65,7 +65,7 @@ module Sepa
65
65
  encrypted_data = iv + encrypted_data
66
66
  encrypted_data = encode encrypted_data
67
67
 
68
- return encrypted_data, key
68
+ [encrypted_data, key]
69
69
  end
70
70
 
71
71
  # Builds the xml structure for the encrypted application request that can be base64 encoded
@@ -1,5 +1,4 @@
1
1
  module Sepa
2
-
3
2
  # Handles Nordea specific response logic. Mainly certificate specific stuff.
4
3
  class NordeaResponse < Response
5
4
  include Utilities
@@ -20,30 +19,18 @@ module Sepa
20
19
  cert.to_s
21
20
  end
22
21
 
23
- # Returns the response code in the response. Overrides {Response#response_code} if {#command} is
24
- # `:get_certificate`, because the namespace is different with that command.
25
- #
26
- # @return [String] response code if it is found
27
- # @return [nil] if response code cannot be found
28
22
  # @see Response#response_code
29
23
  def response_code
30
24
  return super unless [:get_certificate, :renew_certificate].include? command
31
25
 
32
- node = doc.at('xmlns|ResponseCode', xmlns: NORDEA_PKI)
33
- node.content if node
26
+ super(namespace: NORDEA_PKI)
34
27
  end
35
28
 
36
- # Returns the response text in the response. Overrides {Response#response_text} if {#command} is
37
- # `:get_certificate`, because the namespace is different with that command.
38
- #
39
- # @return [String] response text if it is found
40
- # @return [nil] if response text cannot be found
41
29
  # @see Response#response_text
42
30
  def response_text
43
31
  return super unless [:get_certificate, :renew_certificate].include? command
44
32
 
45
- node = doc.at('xmlns|ResponseText', xmlns: NORDEA_PKI)
46
- node.content if node
33
+ super(namespace: NORDEA_PKI)
47
34
  end
48
35
 
49
36
  # Checks whether the certificate embedded in the response soap has been signed with Nordea's
@@ -55,6 +42,5 @@ module Sepa
55
42
  def certificate_is_trusted?
56
43
  verify_certificate_against_root_certificate(certificate, NORDEA_ROOT_CERTIFICATE)
57
44
  end
58
-
59
45
  end
60
46
  end
@@ -6,7 +6,7 @@ module Sepa
6
6
  BYPASS_COMMANDS = %i(
7
7
  get_certificate
8
8
  get_service_certificates
9
- )
9
+ ).freeze
10
10
 
11
11
  # Extracts own signing certificate from the response.
12
12
  #
@@ -24,36 +24,18 @@ module Sepa
24
24
  cert.to_s
25
25
  end
26
26
 
27
- # Returns the response code in the response. Overrides {Response#response_code} if {#command} is
28
- # `:get_certificate`, because the namespace is different with that command.
29
- #
30
- # @return [String] response code if it is found
31
- # @return [nil] if response code cannot be found
32
27
  # @see Response#response_code
33
28
  def response_code
34
- return super unless %i(
35
- get_certificate
36
- get_service_certificates
37
- ).include? command
29
+ return super unless [:get_certificate, :get_service_certificates].include? command
38
30
 
39
- node = doc.at('xmlns|ResponseCode', xmlns: OP_PKI)
40
- node.content if node
31
+ super(namespace: OP_PKI)
41
32
  end
42
33
 
43
- # Returns the response text in the response. Overrides {Response#response_text} if {#command} is
44
- # `:get_certificate`, because the namespace is different with that command.
45
- #
46
- # @return [String] response text if it is found
47
- # @return [nil] if response text cannot be found
48
34
  # @see Response#response_text
49
35
  def response_text
50
- return super unless %i(
51
- get_certificate
52
- get_service_certificates
53
- ).include? command
36
+ return super unless [:get_certificate, :get_service_certificates].include? command
54
37
 
55
- node = doc.at('xmlns|ResponseText', xmlns: OP_PKI)
56
- node.content if node
38
+ super(namespace: OP_PKI)
57
39
  end
58
40
 
59
41
  # Checks whether the certificate embedded in the response soap has been signed with OP's
@@ -0,0 +1,35 @@
1
+ module Sepa
2
+ # Handles Samlink specific response logic. Mainly certificate specific stuff.
3
+ class SamlinkResponse < Response
4
+ # @see Response#response_code
5
+ def response_code
6
+ [:get_certificate, :renew_certificate].include?(command) ? super(namespace: SAMLINK_PKI) : super
7
+ end
8
+
9
+ # @see Response#response_code
10
+ def response_text
11
+ [:get_certificate, :renew_certificate].include?(command) ? super(namespace: SAMLINK_PKI) : super
12
+ end
13
+
14
+ def application_response
15
+ [:get_certificate, :renew_certificate].include?(command) ? super(namespace: SAMLINK_PKI) : super
16
+ end
17
+
18
+ def own_signing_certificate
19
+ (node = Nokogiri::XML(application_response).at('xmlns|Certificate > xmlns|Certificate', xmlns: OP_XML_DATA)) &&
20
+ (content = node.content) &&
21
+ x509_certificate(decode(content)).to_s
22
+ end
23
+
24
+ def certificate_is_trusted?
25
+ case environment
26
+ when :production
27
+ # Samlink doesn't provide a CA certificate for production environment and that's why we check that the
28
+ # certificate provided is equal to the known trusted certificate.
29
+ certificate.to_s == SAMLINK_CERTIFICATE.to_s
30
+ when :test
31
+ verify_certificate_against_root_certificate(certificate, SAMLINK_ROOT_CERTIFICATE)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,14 @@
1
+ module Sepa
2
+ # Contains Samlink specific soap building functionality
3
+ module SamlinkSoapRequest
4
+ private
5
+
6
+ def set_receiver_id
7
+ set_node @template, 'bxd|ReceiverId', @target_id
8
+ end
9
+
10
+ def cert_ns
11
+ SAMLINK_PKI
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIE6TCCAtGgAwIBAgIQA3WFy1naV3PkFL0hKYOZ3TANBgkqhkiG9w0BAQsFADA9
3
+ MQswCQYDVQQGEwJGSTEQMA4GA1UECgwHU2FtbGluazEcMBoGA1UEAwwTU2FtbGlu
4
+ ayBDdXN0b21lciBDQTAeFw0xNDA5MjQwODI0MjJaFw0xNzA5MjMwODI0MjJaMFYx
5
+ CzAJBgNVBAYTAkZJMSEwHwYDVQQKDBhBaW5laXN0b3BhbHZlbHV0LVNhbWxpbmsx
6
+ ETAPBgNVBAMMCFNFUEFXRUJTMREwDwYDVQQEDAgwMDAwMDAwMTCCASIwDQYJKoZI
7
+ hvcNAQEBBQADggEPADCCAQoCggEBANpDNrC4C+2vUuDvDYpvRn14AWW48JlOC8M/
8
+ w5k5/fu8B+sV70qt/no3JKBKNvNpqDr34hILBtCc3TppGDz+Uv/W347Q/N42B1Z9
9
+ 8cAKMEkoKvLcQVotlLSzxXeG5nhsZKw79uiyNx8VePHnnPVjjH1+daOG0xEPZqcL
10
+ XqtJh2dI9OjHs+HWkgy8Eudv0GrO0g5ArrEN1sfNfp9WdhZ7nzHtYcIYVApZB9oV
11
+ MpBkWXBAV3XZNbu+3gHbAs7JNi+c90MOmuhlzUIF6DNUU0ahrTQR55LynkGVuY/v
12
+ jlQvIdzD71bC3MoNZ1yJgoWDi1DjakkdSijiH2lLJtYlKQ/VTVcCAwEAAaOByzCB
13
+ yDAfBgNVHSMEGDAWgBTKgDgzk4pjBJGNBWlWaEI15cf/vDB2BgNVHR8EbzBtMGug
14
+ aaBnhmVsZGFwOi8vMTk0LjI1Mi4xMjQuMjQxOjM4OS9jbj1TYW1saW5rJTIwQ3Vz
15
+ dG9tZXIlMjBDQSxvPVNhbWxpbmssYz1maT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25s
16
+ aXN0O2JpbmFyeTAOBgNVHQ8BAf8EBAMCBPAwHQYDVR0OBBYEFEAoDpdUQbW9K35l
17
+ 2K3ywgNemLeWMA0GCSqGSIb3DQEBCwUAA4ICAQBr0+3BfuQacQ9esTZpny08lagu
18
+ FMttI47i8AUwkJkKnvhROysBke77UoO6jyHDxskFOHd9yZdnPfzpJ6AVXzqwUxdL
19
+ Uakk38Wk8ODAyF6leRuy+I/biO+jRa52C0Ai4+XF5VNa1UUrsvjYJl6BqKJmI8aO
20
+ 34ArzOcFocUF9UBQNC/eqL/AIx8mp5HC6fWA58kvWwiwdC5CQILbMMADpDjmEpkg
21
+ 2ueBsLudrxXw1uNktY2wUv95KcfmlqdeAPkb0ra5x7p5SM1bJYFh8MMpm9BwnJ3a
22
+ q/l1+qFMmL0GCcevNQP5Dp3Vkrsf601aQcYyaptHMfiv6ryLfVq3487gUPNPUwWn
23
+ x24K1A5j1aQBmF8TLZl8MRkDeeEIwbXJxgoZZXAJSO2Yf/JO/vPCalBJflosiNgg
24
+ o9oxE8LCjqbJ26HeiNoeNUC1cnv15yhb6O+eSgZbkAYfuKgLWeOele3oC2QWqqT7
25
+ TSHaEipRz3TifdzpzKuXRvQaPv6lra28nbyc+OeJlnr/A7ZH3VF6OkasAoIC5wmU
26
+ tJgcQW5eD5jmaQZYktZljNWKXEm6tXmXOnDoRib0yzqUA9s6ReD7sBHBUaTDZFfy
27
+ /qrAHfYk4Pe/9seeBmbTnkdi5VnoNQT70boqaVsa/OK8eE2EWjTYs++ocfWaGSpN
28
+ pUI4al2qFueN8ji+nw==
29
+ -----END CERTIFICATE-----
@@ -0,0 +1,32 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ yMgRk0vap01P6bNkJgj/PrkOw/ECDQ==
32
+ -----END CERTIFICATE-----
data/lib/sepa/client.rb CHANGED
@@ -162,11 +162,27 @@ module Sepa
162
162
  # @see #signing_csr The format is the same as in signing csr
163
163
  attr_accessor :encryption_csr
164
164
 
165
+ # Options to be passed directly to the underlying savon client. Format is as follows:
166
+ # {
167
+ # globals: {
168
+ # ssl_verify_mode: :none,
169
+ # ...,
170
+ # },
171
+ # locals: {
172
+ # xml: "<envelope></envelope>",
173
+ # ...,
174
+ # },
175
+ # }
176
+ #
177
+ # @return [Hash]
178
+ attr_accessor :savon_options
179
+
165
180
  # The list of banks that are currently supported by this gem
166
181
  BANKS = %i(
167
182
  danske
168
183
  nordea
169
184
  op
185
+ samlink
170
186
  ).freeze
171
187
 
172
188
  # Languages that are currently supported by the gem
@@ -204,9 +220,13 @@ module Sepa
204
220
  # @param hash [Hash] All the attributes of the client can be given to the construcor in a hash
205
221
  def initialize(hash = {})
206
222
  attributes(hash)
207
- self.environment ||= :production
208
- self.language ||= 'EN'
209
- self.status ||= 'NEW'
223
+ self.environment ||= :production
224
+ self.language ||= 'EN'
225
+ self.status ||= 'NEW'
226
+ self.savon_options ||= {
227
+ globals: {},
228
+ locals: {},
229
+ }
210
230
  end
211
231
 
212
232
  def bank=(value)
@@ -249,12 +269,11 @@ module Sepa
249
269
  def send_request
250
270
  raise ArgumentError, errors.messages unless valid?
251
271
 
252
- soap = SoapBuilder.new(create_hash).to_xml
253
- client = Savon.client(wsdl: wsdl)
272
+ client = Savon.client(savon_globals)
254
273
 
255
274
  begin
256
275
  error = nil
257
- response = client.call(soap_command, xml: soap)
276
+ response = client.call(soap_command, savon_locals)
258
277
  response &&= response.to_xml
259
278
  rescue Savon::Error => e
260
279
  response = nil
@@ -318,7 +337,7 @@ module Sepa
318
337
  command: command,
319
338
  environment: environment,
320
339
  error: error,
321
- response: response
340
+ response: response,
322
341
  }
323
342
  if encryption_private_key && !encryption_private_key.empty?
324
343
  options[:encryption_private_key] = rsa_key(encryption_private_key)
@@ -328,11 +347,19 @@ module Sepa
328
347
  end
329
348
 
330
349
  def soap_command
331
- if @command == :renew_certificate && [:nordea, :op].include?(@bank)
350
+ if @command == :renew_certificate && [:nordea, :op, :samlink].include?(@bank)
332
351
  :get_certificate
333
352
  else
334
353
  @command
335
354
  end
336
355
  end
356
+
357
+ def savon_globals
358
+ { wsdl: wsdl }.merge(savon_options[:globals] || {})
359
+ end
360
+
361
+ def savon_locals
362
+ { xml: SoapBuilder.new(create_hash).to_xml }.merge(savon_options[:locals] || {})
363
+ end
337
364
  end
338
365
  end