Class: Sepa::Response
- Inherits:
-
Object
- Object
- Sepa::Response
- Includes:
- ActiveModel::Validations, ErrorMessages, Utilities
- Defined in:
- lib/sepa/response.rb
Overview
Handles soap responses got back from the bank. Bank specific functionality is defined in subclasses. Handles i.e. logic to make sure the response's integrity has not been compromised and has methods to extract content from the response.
Direct Known Subclasses
Constant Summary
Constants included from ErrorMessages
ErrorMessages::CONTENT_ERROR_MESSAGE, ErrorMessages::CUSTOMER_ID_ERROR_MESSAGE, ErrorMessages::DECRYPTION_ERROR_MESSAGE, ErrorMessages::ENCRYPTION_CERT_ERROR_MESSAGE, ErrorMessages::ENCRYPTION_CERT_REQUEST_ERROR_MESSAGE, ErrorMessages::ENCRYPTION_PRIVATE_KEY_ERROR_MESSAGE, ErrorMessages::ENVIRONMENT_ERROR_MESSAGE, ErrorMessages::FILE_REFERENCE_ERROR_MESSAGE, ErrorMessages::FILE_TYPE_ERROR_MESSAGE, ErrorMessages::HASH_ERROR_MESSAGE, ErrorMessages::NOT_OK_RESPONSE_CODE_ERROR_MESSAGE, ErrorMessages::PIN_ERROR_MESSAGE, ErrorMessages::SIGNATURE_ERROR_MESSAGE, ErrorMessages::SIGNING_CERT_REQUEST_ERROR_MESSAGE, ErrorMessages::STATUS_ERROR_MESSAGE, ErrorMessages::TARGET_ID_ERROR_MESSAGE
Instance Attribute Summary collapse
-
#command ⇒ Symbol
readonly
The command with which the response was initialized.
-
#environment ⇒ Symbol
readonly
The environment in which the request was sent.
-
#error ⇒ String
readonly
Possible Savon::Error with which the Response was initialized.
-
#soap ⇒ String
readonly
The raw soap response in xml.
Instance Method Summary collapse
-
#application_response(namespace: BXD) ⇒ String
Gets the application response from the response as an xml document.
- #bank_encryption_certificate ⇒ Object abstract
- #bank_root_certificate ⇒ Object abstract
- #bank_signing_certificate ⇒ Object abstract
- #ca_certificate ⇒ Object abstract
-
#certificate ⇒ OpenSSL::X509::Certificate?
Returns the certificate embedded in the response.
-
#client_errors ⇒ Object
private
Handles errors that have been passed from client.
-
#content ⇒ String
Returns the content of the response according to #command.
-
#doc ⇒ Nokogiri::XML
Returns the soap of the response as a Nokogiri document.
-
#document_must_validate_against_schema ⇒ Object
private
Validates the document against soap schema unless #error is present or command is
:get_bank_certificate
. -
#error_doc ⇒ Nokogiri::XML
Returns the error of the response as a Nokogiri document.
-
#extract_application_response(namespace) ⇒ String?
private
Extracts and returns application response from the response.
-
#file_references ⇒ Array
Returns the file references in a download file list response.
-
#find_digest_values ⇒ Hash
private
Finds all reference nodes with digest values in the document and returns a hash with uri as the key and digest as the value.
-
#find_node_by_uri(uri) ⇒ Nokogiri::XML::Node
private
Find node by it's reference URI in soap header.
-
#find_nodes_to_verify(references) ⇒ Hash
private
Finds nodes to verify by comparing their id's to the uris' in the references hash.
-
#hashes_match?(options = {}) ⇒ false, true
Verifies that all digest values in the response match the actual ones.
-
#initialize(hash = {}) ⇒ Response
constructor
Initializes the response with a options hash.
- #own_encryption_certificate ⇒ Object abstract
- #own_signing_certificate ⇒ Object abstract
-
#response_code(namespace: BXD, node_name: 'ResponseCode') ⇒ String?
Returns the response code of the response.
-
#response_code_is_ok? ⇒ true, false
private
Checks whether response code in the response is ok.
-
#response_text(namespace: BXD, node_name: 'ResponseText') ⇒ String?
Returns the response text of the response.
-
#signature_is_valid? ⇒ true, false
Verifies the signature by extracting the public key from the certificate embedded in the response and verifying the signature value with that.
-
#to_s ⇒ String
Returns the raw soap as xml.
-
#validate_hashes ⇒ Object
private
Validates hashes in the response.
-
#validate_response_code ⇒ Object
private
Validates response code in response.
-
#verify_certificate ⇒ Object
private
Validates certificate in the soap.
-
#verify_signature ⇒ Object
private
Validate signature in the response.
Methods included from Utilities
#calculate_digest, #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(hash = {}) ⇒ Response
Initializes the response with a options hash
47 48 49 50 51 52 53 |
# File 'lib/sepa/response.rb', line 47 def initialize(hash = {}) @command = hash[:command] @encryption_private_key = hash[:encryption_private_key] @environment = hash[:environment] @error = hash[:error] @soap = hash[:response] end |
Instance Attribute Details
#command ⇒ Symbol (readonly)
The command with which the response was initialized
23 24 25 |
# File 'lib/sepa/response.rb', line 23 def command @command end |
#environment ⇒ Symbol (readonly)
The environment in which the request was sent
28 29 30 |
# File 'lib/sepa/response.rb', line 28 def environment @environment end |
#error ⇒ String (readonly)
Possible Savon::Error with which the Sepa::Response was initialized
18 19 20 |
# File 'lib/sepa/response.rb', line 18 def error @error end |
#soap ⇒ String (readonly)
The raw soap response in xml
13 14 15 |
# File 'lib/sepa/response.rb', line 13 def soap @soap end |
Instance Method Details
#application_response(namespace: BXD) ⇒ String
Gets the application response from the response as an xml document. Makes a call to #extract_application_response to do the extraction.
124 125 126 |
# File 'lib/sepa/response.rb', line 124 def application_response(namespace: BXD) @application_response ||= extract_application_response(namespace) end |
#bank_encryption_certificate ⇒ Object
189 |
# File 'lib/sepa/response.rb', line 189 def bank_encryption_certificate; end |
#bank_root_certificate ⇒ Object
195 |
# File 'lib/sepa/response.rb', line 195 def bank_root_certificate; end |
#bank_signing_certificate ⇒ Object
192 |
# File 'lib/sepa/response.rb', line 192 def bank_signing_certificate; end |
#ca_certificate ⇒ Object
204 |
# File 'lib/sepa/response.rb', line 204 def ca_certificate; end |
#certificate ⇒ OpenSSL::X509::Certificate?
Returns the certificate embedded in the response
146 147 148 149 150 |
# File 'lib/sepa/response.rb', line 146 def certificate @certificate ||= begin extract_cert(doc, 'BinarySecurityToken', OASIS_SECEXT) end end |
#client_errors ⇒ Object (private)
Handles errors that have been passed from client
281 282 283 284 |
# File 'lib/sepa/response.rb', line 281 def client_errors client_error = error.to_s errors.add(:base, client_error) unless client_error.empty? end |
#content ⇒ String
Returns the content of the response according to #command. When command is :download_file
,
content is returned as a base64 encoded string, when #command is :download_file_list
, the
content is returned as xml, when #command is :get_user_info
, the content is returned as xml
and when #command is :upload_file
, content is returned as xml
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/sepa/response.rb', line 158 def content @content ||= begin xml = xml_doc(application_response) case @command when :download_file content_node = xml.at('xmlns|Content', xmlns: XML_DATA) content_node.content if content_node when :download_file_list content_node = xml.remove_namespaces!.at('FileDescriptors') content_node.to_xml if content_node when :get_user_info canonicalized_node(xml, XML_DATA, 'UserFileTypes') when :upload_file signature_node = xml.at('xmlns|Signature', xmlns: DSIG) if signature_node signature_node.remove xml.canonicalize end end end end |
#doc ⇒ Nokogiri::XML
Returns the soap of the response as a Nokogiri document
58 59 60 |
# File 'lib/sepa/response.rb', line 58 def doc @doc = @soap ? xml_doc(@soap) : xml_doc(@error) end |
#document_must_validate_against_schema ⇒ Object (private)
Validates the document against soap schema unless #error is present or command is
:get_bank_certificate
262 263 264 265 266 |
# File 'lib/sepa/response.rb', line 262 def document_must_validate_against_schema return if @error || command.to_sym == :get_bank_certificate check_validity_against_schema(doc, 'soap.xsd') end |
#error_doc ⇒ Nokogiri::XML
Returns the error of the response as a Nokogiri document
65 66 67 |
# File 'lib/sepa/response.rb', line 65 def error_doc @error_doc ||= xml_doc @error end |
#extract_application_response(namespace) ⇒ String? (private)
Extracts and returns application response from the response
272 273 274 275 276 277 278 |
# File 'lib/sepa/response.rb', line 272 def extract_application_response(namespace) ar_node = doc.at('xmlns|ApplicationResponse', xmlns: namespace) return unless ar_node decode(ar_node.content) end |
#file_references ⇒ Array
Returns the file references in a download file list response
131 132 133 134 135 136 137 138 139 |
# File 'lib/sepa/response.rb', line 131 def file_references return unless @command == :download_file_list @file_references ||= begin xml = xml_doc content descriptors = xml.css('FileDescriptor') descriptors.map { |descriptor| descriptor.at('FileReference').content } end end |
#find_digest_values ⇒ Hash (private)
Finds all reference nodes with digest values in the document and returns a hash with uri as the key and digest as the value.
228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/sepa/response.rb', line 228 def find_digest_values references = {} reference_nodes = doc.css('xmlns|Reference', xmlns: DSIG) reference_nodes.each do |node| uri = node.attr('URI') digest_value = node.at('xmlns|DigestValue', xmlns: DSIG).content references[uri] = digest_value end references end |
#find_node_by_uri(uri) ⇒ Nokogiri::XML::Node (private)
Find node by it's reference URI in soap header
290 291 292 |
# File 'lib/sepa/response.rb', line 290 def find_node_by_uri(uri) doc.at("[xmlns|Id='#{uri}']", xmlns: OASIS_UTILITY) end |
#find_nodes_to_verify(references) ⇒ Hash (private)
Finds nodes to verify by comparing their id's to the uris' in the references hash. Then calculates the hashes of those nodes and returns them in a hash
247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/sepa/response.rb', line 247 def find_nodes_to_verify(references) nodes = {} references.each do |uri, _digest_value| uri = uri.sub(/^#/, '') node = find_node_by_uri(uri) nodes[uri] = calculate_digest(node) end nodes end |
#hashes_match?(options = {}) ⇒ false, true
Verifies that all digest values in the response match the actual ones. Takes an optional verbose parameter to show which digests didn't match. The digest embedded in the document are first retrieved with #find_digest_values method and if none are found, false is returned. After this, nodes to calculate hashes from are retrieved and hashes using #find_nodes_to_verify method and after this the calculated digests are compared with the embedded ones. If the all match, true is returned. If some digests failed to verify and verbose parameter was passed, digests that failed to verify are printed to screen and false is returned. Otherwise just false is returned.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/sepa/response.rb', line 83 def hashes_match?( = {}) digests = find_digest_values return false if digests.empty? nodes = find_nodes_to_verify(digests) verified_digests = digests.select do |uri, digest| uri = uri.sub(/^#/, '') digest == nodes[uri] end return true if digests == verified_digests unverified_digests = digests.select do |uri, digest| uri = uri.sub(/^#/, '') digest != nodes[uri] end if [:verbose] puts "These digests failed to verify: #{unverified_digests}" end false end |
#own_encryption_certificate ⇒ Object
198 |
# File 'lib/sepa/response.rb', line 198 def own_encryption_certificate; end |
#own_signing_certificate ⇒ Object
201 |
# File 'lib/sepa/response.rb', line 201 def own_signing_certificate; end |
#response_code(namespace: BXD, node_name: 'ResponseCode') ⇒ String?
Returns the response code of the response
210 211 212 |
# File 'lib/sepa/response.rb', line 210 def response_code(namespace: BXD, node_name: 'ResponseCode') (node = doc.at("xmlns|#{node_name}", xmlns: namespace)) && node.content && node.content.rjust(2, '0') end |
#response_code_is_ok? ⇒ true, false (private)
Checks whether response code in the response is ok. Response code is considered ok if it is "00" or "24".
331 332 333 334 335 |
# File 'lib/sepa/response.rb', line 331 def response_code_is_ok? return true if %w(00 24).include? response_code false end |
#response_text(namespace: BXD, node_name: 'ResponseText') ⇒ String?
Returns the response text of the response
218 219 220 |
# File 'lib/sepa/response.rb', line 218 def response_text(namespace: BXD, node_name: 'ResponseText') (node = doc.at("xmlns|#{node_name}", xmlns: namespace)) && node.content end |
#signature_is_valid? ⇒ true, false
Verifies the signature by extracting the public key from the certificate embedded in the
response and verifying the signature value with that. Makes a call to Utilities#validate_signature
to do the actual verification. Passes :exclusive
to Utilities#validate_signature so that exclusive
mode of xml canonicalization is used.
116 117 118 |
# File 'lib/sepa/response.rb', line 116 def signature_is_valid? validate_signature(doc, certificate, :exclusive) end |
#to_s ⇒ String
Returns the raw soap as xml
184 185 186 |
# File 'lib/sepa/response.rb', line 184 def to_s @soap end |
#validate_hashes ⇒ Object (private)
Validates hashes in the response. #hashes_match? must return true for validation to pass. Is not run if #error is present or response code is not ok.
303 304 305 306 307 |
# File 'lib/sepa/response.rb', line 303 def validate_hashes return if @error || !response_code_is_ok? || hashes_match? errors.add(:base, HASH_ERROR_MESSAGE) end |
#validate_response_code ⇒ Object (private)
Validates response code in response. "00" and "24" are currently considered valid.
295 296 297 298 299 |
# File 'lib/sepa/response.rb', line 295 def validate_response_code return if %w(00 24).include? response_code errors.add(:base, response_code: response_code, response_text: response_text) end |
#verify_certificate ⇒ Object (private)
Validates certificate in the soap. The certificate must be present and signed by the bank's root certificate for the validation to pass. Is not run if #error is present or response code is not ok.
320 321 322 323 324 |
# File 'lib/sepa/response.rb', line 320 def verify_certificate return if @error || !response_code_is_ok? || certificate_is_trusted? errors.add(:base, 'The certificate in the response is not trusted') end |
#verify_signature ⇒ Object (private)
Validate signature in the response. Validation is not run if #error is present or response is not ok.
311 312 313 314 315 |
# File 'lib/sepa/response.rb', line 311 def verify_signature return if @error || !response_code_is_ok? || signature_is_valid? errors.add(:base, SIGNATURE_ERROR_MESSAGE) end |