Class: Sepa::ApplicationRequest

Inherits:
Object
  • Object
show all
Includes:
Utilities
Defined in:
lib/sepa/application_request.rb

Overview

TODO:

Add return values for content modifying methods to signal whether they succeeded or not

Contains functionality to build the application request

Instance Method Summary collapse

Methods included from Utilities

#canonicalize_exclusively, #canonicalized_node, #cert_request_valid?, #check_validity_against_schema, #csr_to_binary, #decode, #encode, #extract_cert, #format_cert, #format_cert_request, #hmac, #iso_time, #load_body_template, #process_cert_value, #rsa_key, #set_node_id, #validate_signature, #verify_certificate_against_root_certificate, #x509_certificate, #xml_doc

Constructor Details

#initialize(params = {}) ⇒ ApplicationRequest

TODO:

Consider not using instance_variable_set so that all the available instance variables can easily be seen.

Initializes the Sepa::ApplicationRequest with a params hash. The application request is usually initialized by the SoapBuilder. The xml template of the application request is also loaded here.

Parameters:

  • params (Hash) (defaults to: {})

    the hash containing attributes needed by the Sepa::ApplicationRequest. All the key => value pairs in the hash are initialized as instance variables. The hash in the initialization is usually the same as with SoapBuilder so the values have already been validated by the client.



18
19
20
21
22
23
24
25
# File 'lib/sepa/application_request.rb', line 18

def initialize(params = {})
  # Set all params as instance variables
  params.each do |key, value|
    instance_variable_set("@#{key}", value)
  end

  @application_request = load_body_template(AR_TEMPLATE_PATH)
end

Instance Method Details

#add_node_after(node, new_node, content:) ⇒ Object (private)



258
259
260
261
262
# File 'lib/sepa/application_request.rb', line 258

def add_node_after(node, new_node, content:)
  new_node = Nokogiri::XML::Node.new(new_node, @application_request)
  new_node.content = content
  @application_request.at(node).add_next_sibling(new_node)
end

#add_node_to_root(node, content: nil) ⇒ Object (private)

Adds node to the root of the application request and content to it if specified



194
195
196
197
198
199
200
201
202
# File 'lib/sepa/application_request.rb', line 194

def add_node_to_root(node, content: nil)
  unless node.is_a? Nokogiri::XML::Node
    node = Nokogiri::XML::Node.new node, @application_request
  end

  @application_request.root.add_child node

  set_node(node.name, content) if content
end

#add_value_to_signature(node, value) ⇒ Object (private)

TODO:

Remove this method and use #set_node method

Adds value to signature node

Parameters:

  • node (String)

    name of the signature node

  • value (#to_s)

    the value to be set to the node



219
220
221
222
223
# File 'lib/sepa/application_request.rb', line 219

def add_value_to_signature(node, value)
  dsig = 'http://www.w3.org/2000/09/xmldsig#'
  sig = @application_request.at_css("dsig|#{node}", 'dsig' => dsig)
  sig.content = value
end

#calculate_digestString (private)

TODO:

Use the digest calculation method in Utilities instead of implementing the functionality again here.

Calculates the digest of #application_request

Returns:

  • (String)

    the base64 encoded digest of the #application_request



209
210
211
212
# File 'lib/sepa/application_request.rb', line 209

def calculate_digest
  sha1 = OpenSSL::Digest::SHA1.new
  encode(sha1.digest(@application_request.canonicalize(canonicalization_mode)))
end

#calculate_signatureString (private)

TODO:

Move to Utilities

Calculates the application request's signature value. Uses #signing_private_key for the calculation.

Returns:

  • (String)

    the base64 encoded signature



230
231
232
233
234
235
236
# File 'lib/sepa/application_request.rb', line 230

def calculate_signature
  sha1 = OpenSSL::Digest::SHA1.new
  dsig = 'http://www.w3.org/2000/09/xmldsig#'
  node = @application_request.at_css("dsig|SignedInfo", 'dsig' => dsig)
  signature = @signing_private_key.sign(sha1, node.canonicalize(canonicalization_mode))
  encode signature
end

#canonicalization_modeObject (private)



264
265
266
267
268
# File 'lib/sepa/application_request.rb', line 264

def canonicalization_mode
  return Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0 if @bank == :danske && @command == :renew_certificate

  Nokogiri::XML::XML_C14N_1_0
end

#pretty_commandObject (private)

Converts #command to string, removes underscores and capitalizes it.

Examples:

Example input and output

:get_user_info --> GetUserInfo


76
77
78
# File 'lib/sepa/application_request.rb', line 76

def pretty_command
  @command.to_s.split(/[\W_]/).map(&:capitalize).join
end

#process_signatureObject (private)

Removes signature from the application request, calculates the application request's digest, calculates the signature and adds needed values to signature node. Also adds #own_signing_certificate to the signature node.



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/sepa/application_request.rb', line 241

def process_signature
  # No signature for Certificate Requests
  return if %i(
    create_certificate
    get_bank_certificate
    get_certificate
    get_service_certificates
  ).include? @command

  signature_node = remove_node('Signature', 'http://www.w3.org/2000/09/xmldsig#')
  digest = calculate_digest
  add_node_to_root(signature_node)
  add_value_to_signature('DigestValue', digest)
  add_value_to_signature('SignatureValue', calculate_signature)
  add_value_to_signature('X509Certificate', format_cert(@own_signing_certificate))
end

#remove_node(node, xmlns) ⇒ Object (private)

TODO:

Move to Utilities and move document to parameters

Removes a node from #application_request

Parameters:

  • node (String)

    name of the node to remove

  • xmlns (String)

    the namespace of the node



189
190
191
# File 'lib/sepa/application_request.rb', line 189

def remove_node(node, xmlns)
  @application_request.at_css("xmlns|#{node}", 'xmlns' => xmlns).remove
end

#set_common_nodesObject (private)

Sets contents for nodes that are common to all requests except when #command is :get_bank_certificate or :create_certificate. #environment is upcased here.



173
174
175
176
177
178
179
180
181
182
# File 'lib/sepa/application_request.rb', line 173

def set_common_nodes
  return if [:get_bank_certificate, :create_certificate].include?(@command)
  return if @bank == :danske && @command == :renew_certificate

  set_node('Environment', @environment.to_s.upcase)
  set_node("CustomerId", @customer_id)
  set_node("Timestamp", iso_time)
  set_node("SoftwareId", "Sepa Transfer Library #{VERSION}")
  set_node("Command", pretty_command) unless @command == :renew_certificate
end

#set_create_certificate_nodesObject (private)

TODO:

Raise error if #bank is other than Nordea like in #set_get_bank_certificate_nodes

Sets nodes' contents for Danske Bank's create certificate request. Environment is set to customertest if #environment is :test



157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/sepa/application_request.rb', line 157

def set_create_certificate_nodes
  set_node("tns|CustomerId", @customer_id)
  set_node("tns|KeyGeneratorType", 'software')
  set_node("tns|EncryptionCertPKCS10", format_cert_request(@encryption_csr))
  set_node("tns|SigningCertPKCS10", format_cert_request(@signing_csr))
  set_node("tns|Timestamp", iso_time)
  set_node("tns|RequestId", @request_id)

  @environment = 'customertest' if @environment == :test
  set_node("tns|Environment", @environment)

  set_node("tns|PIN", @pin)
end

#set_download_file_list_nodesObject (private)

Sets nodes' contents for download file list request



117
118
119
120
121
# File 'lib/sepa/application_request.rb', line 117

def set_download_file_list_nodes
  add_node_after('Environment', 'TargetId', content: @target_id) if @bank == :nordea
  add_node_after('Timestamp', 'Status', content: @status) if @status.present?
  add_node_to_root 'FileType', content: @file_type if @file_type.present?
end

#set_download_file_nodesObject (private)

Sets nodes' values for download file request



88
89
90
91
92
93
# File 'lib/sepa/application_request.rb', line 88

def set_download_file_nodes
  add_node_after('FileReferences', 'TargetId', content: @target_id) if @bank == :nordea
  add_node_after('Timestamp', 'Status', content: @status) if @status.present?
  add_node_to_root 'FileType', content: @file_type if @file_type.present?
  set_node("FileReference", @file_reference)
end

#set_get_bank_certificate_nodesObject (private)

TODO:

Investigate a better way to set the bank's root certificate's serial instead of hardcoding it

Sets Danske Bank's get bank certificate request's contents

Raises:

  • (OnlyWorksWithDanske)

    if #bank is not danske



100
101
102
103
104
105
106
107
# File 'lib/sepa/application_request.rb', line 100

def set_get_bank_certificate_nodes
  raise 'OnlyWorksWithDanske' if @bank != :danske

  # Root Cert Serial Hardcoded to Danske
  set_node("elem|BankRootCertificateSerialNo", '1111110002')
  set_node("elem|Timestamp", iso_time)
  set_node("elem|RequestId", @request_id)
end

#set_get_certificate_nodesObject (private)

Sets nodes' contents for Nordea's and OP's get certificate request



124
125
126
127
128
129
# File 'lib/sepa/application_request.rb', line 124

def set_get_certificate_nodes
  set_node "Service", "MATU" if @bank == :op
  set_node "TransferKey", @pin if [:op, :samlink].include?(@bank)
  set_node "HMAC", hmac(@pin, csr_to_binary(@signing_csr)) if @bank == :nordea
  set_node "Content", format_cert_request(@signing_csr)
end

#set_node(node, value) ⇒ Object (private)

Sets node to value

Parameters:

  • node (String)

    the name of the node which value is to be set

  • value (#to_s)

    the value which is going to be set to the node



59
60
61
# File 'lib/sepa/application_request.rb', line 59

def set_node(node, value)
  @application_request.at_css(node).content = value
end

#set_node_b(node, value) ⇒ Object (private)

TODO:

rename

Sets node to base64 encoded value

Parameters:

  • node (String)

    name of the node

  • value (#to_s)

    the value which is going to be set to the nodea base64 encoded



68
69
70
# File 'lib/sepa/application_request.rb', line 68

def set_node_b(node, value)
  set_node node, encode(value)
end

#set_nodes_contentsObject (private)

Determines which content setting method to call depending on #command



81
82
83
84
85
# File 'lib/sepa/application_request.rb', line 81

def set_nodes_contents
  method = "set_#{@command}_nodes"

  send(method) if self.class.private_method_defined? method
end

#set_renew_certificate_nodesObject (private)

Sets nodes' contents for renew certificate request



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/sepa/application_request.rb', line 132

def set_renew_certificate_nodes
  case @bank
  when :nordea, :op, :samlink
    set_node "Service", "service" if @bank == :nordea
    set_node "Content", format_cert_request(@signing_csr)
  when :danske
    @environment = 'customertest' if @environment == :test

    set_node 'tns|CustomerId',           @customer_id
    set_node 'tns|EncryptionCertPKCS10', format_cert_request(@encryption_csr)
    set_node 'tns|SigningCertPKCS10',    format_cert_request(@signing_csr)
    set_node 'tns|Timestamp',            iso_time
    set_node 'tns|Environment',          @environment
  end
end

#set_service_certificates_nodesObject (private)

Sets nodes' contents for OP's get service certificates request



149
150
151
# File 'lib/sepa/application_request.rb', line 149

def set_service_certificates_nodes
  set_node("Service", "MATU")
end

#set_upload_file_nodesObject (private)

Sets nodes' contents for upload file request



110
111
112
113
114
# File 'lib/sepa/application_request.rb', line 110

def set_upload_file_nodes
  set_node_b("Content", @content)
  set_node("FileType", @file_type)
  add_node_after('Environment', 'TargetId', content: @target_id) if @bank == :nordea
end

#to_base64String

Base64 encodes the whole application request

Returns:

  • (String)

    the base64 encoded application request



42
43
44
# File 'lib/sepa/application_request.rb', line 42

def to_base64
  encode to_xml
end

#to_nokogiriNokogiri::XML::Document

Returns the application request as a Nokogiri document

Returns:

  • (Nokogiri::XML::Document)

    the application request as a nokogiri document



49
50
51
# File 'lib/sepa/application_request.rb', line 49

def to_nokogiri
  Nokogiri::XML to_xml
end

#to_xmlString

TODO:

This method is obviously doing too much

Sets the nodes in the application request, processes signature and then returns the application request as an xml document.

Returns:

  • (String)

    the application request as an xml document



32
33
34
35
36
37
# File 'lib/sepa/application_request.rb', line 32

def to_xml
  set_common_nodes
  set_nodes_contents
  process_signature
  @application_request.to_xml
end