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.



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

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_to_root(node, content: nil) ⇒ Object (private)

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



178
179
180
181
182
183
184
185
186
# File 'lib/sepa/application_request.rb', line 178

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_target_id_after(node) ⇒ Object (private)

Adds target id to the application request after a specific node because the schema defines a sequence. Target id is only added if #bank is :nordea

Parameters:

  • node (String)

    the name of the node after which the target id node will be added



246
247
248
249
250
251
252
# File 'lib/sepa/application_request.rb', line 246

def add_target_id_after(node)
  return unless @bank == :nordea

  target_id = Nokogiri::XML::Node.new 'TargetId', @application_request
  target_id.content = @target_id
  @application_request.at(node).add_next_sibling target_id
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



203
204
205
206
207
# File 'lib/sepa/application_request.rb', line 203

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



193
194
195
196
# File 'lib/sepa/application_request.rb', line 193

def calculate_digest
  sha1 = OpenSSL::Digest::SHA1.new
  encode(sha1.digest(@application_request.canonicalize))
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



214
215
216
217
218
219
220
# File 'lib/sepa/application_request.rb', line 214

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)
  encode signature
end

#pretty_commandObject (private)

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

Examples:

Example input and output

:get_user_info --> GetUserInfo


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

def pretty_command
  @command.to_s.split(/[\W_]/).map {|c| c.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.



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/sepa/application_request.rb', line 225

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



173
174
175
# File 'lib/sepa/application_request.rb', line 173

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.



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

def set_common_nodes
  return if @command == :get_bank_certificate
  return if @command == :create_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 #{VERSION}")
  set_node("Command", pretty_command)
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



141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/sepa/application_request.rb', line 141

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



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

def set_download_file_list_nodes
  add_target_id_after 'Environment'
  set_node("Status", @status)
  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



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

def set_download_file_nodes
  add_target_id_after 'FileReferences'
  set_node("Status", @status)
  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



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

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



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

def set_get_certificate_nodes
  set_node "Service", "MATU" if @bank == :op
  set_node "TransferKey", @pin if @bank == :op
  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



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

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



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

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



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

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

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

#set_service_certificates_nodesObject (private)

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



133
134
135
# File 'lib/sepa/application_request.rb', line 133

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

#set_upload_file_nodesObject (private)

Sets nodes' contents for upload file request



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

def set_upload_file_nodes
  set_node_b("Content", @content)
  set_node("FileType", @file_type)
  add_target_id_after 'Environment'
end

#to_base64String

Base64 encodes the whole application request

Returns:

  • (String)

    the base64 encoded application request



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

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



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

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



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

def to_xml
  set_common_nodes
  set_nodes_contents
  process_signature
  @application_request.to_xml
end