class Sepa::SoapBuilder
Builds a soap message with given parameters. This class is extended with proper bank module depending on bank.
Attributes
Application request built with the same parameters as the soap
@return [ApplicationRequest]
Public Class Methods
Initializes the {SoapBuilder} with the params hash and then extends the {SoapBuilder} with the correct bank module. The {SoapBuilder} class is usually created by the client which handles parameter validation.
@param params [Hash] options hash
# File lib/sepa/soap_builder.rb, line 17 def initialize(params) @bank = params[:bank] @own_signing_certificate = params[:own_signing_certificate] @command = params[:command] @content = params[:content] @customer_id = params[:customer_id] @bank_encryption_certificate = params[:bank_encryption_certificate] @environment = params[:environment] @file_reference = params[:file_reference] @file_type = params[:file_type] @language = params[:language] @signing_private_key = params[:signing_private_key] @status = params[:status] @target_id = params[:target_id] @application_request = ApplicationRequest.new params @header_template = load_header_template @template = load_body_template SOAP_TEMPLATE_PATH find_correct_bank_extension end
Public Instance Methods
Returns the soap as raw xml
@return [String] the soap as xml
# File lib/sepa/soap_builder.rb, line 42 def to_xml find_correct_build.to_xml end
Private Instance Methods
Adds soap body to header template
@return [Nokogiri::XML] the soap with added body as a nokogiri document
# File lib/sepa/soap_builder.rb, line 149 def add_body_to_header body = @template.at_css('env|Body') @header_template.root.add_child(body) @header_template end
Sets contents for certificate request
@return [Nokogiri::XML] the template with contents added to it
# File lib/sepa/soap_builder.rb, line 78 def build_certificate_request set_body_contents end
Builds generic request which is a request made with commands:
-
Get User Info
-
Download File
-
Download File List
-
Upload File
@return [Nokogiri::XML] the generic request soap
# File lib/sepa/soap_builder.rb, line 68 def build_common_request common_set_body_contents set_receiver_id process_header add_body_to_header end
Calculates digest hash for the given node in the given document. The node is canonicalized exclusively before digest calculation.
@param doc [Nokogiri::XML] Document that contains the node @param node [String] The name of the node @return [String] the base64 encoded string @todo remove this method and use {Utilities#calculate_digest}
# File lib/sepa/soap_builder.rb, line 101 def calculate_digest(doc, node) sha1 = OpenSSL::Digest::SHA1.new node = doc.at_css(node) canon_node = canonicalize_exclusively(node) encode(sha1.digest(canon_node)).gsub(/\s+/, "") end
Calculates signature for the given node in the given document. Uses the signing private key given to SoapBuilder
for the signing. The node is canonicalized exclusively before signature calculation.
@param doc [Nokogiri::XML] Document that contains the node @param node [String] Name of the node to calculate signature from @return [String] the base64 encoded signature @todo refactor to use canonicalization from utilities
# File lib/sepa/soap_builder.rb, line 117 def calculate_signature(doc, node) sha1 = OpenSSL::Digest::SHA1.new node = doc.at_css(node) canon_signed_info_node = canonicalize_exclusively(node) signature = @signing_private_key.sign(sha1, canon_signed_info_node) encode(signature).gsub(/\s+/, "") end
Sets nodes for generic requests, application request is base64 encoded here.
# File lib/sepa/soap_builder.rb, line 208 def common_set_body_contents set_application_request set_node @template, 'bxd|SenderId', @customer_id set_node @template, 'bxd|RequestId', request_id set_node @template, 'bxd|Timestamp', iso_time set_node @template, 'bxd|Language', @language set_node @template, 'bxd|UserAgent', "Sepa Transfer Library #{VERSION}" end
Extends the class with proper module depending on bank
# File lib/sepa/soap_builder.rb, line 49 def find_correct_bank_extension extend("Sepa::#{@bank.capitalize}SoapRequest".constantize) end
Determines which soap request to build based on command. Certificate requests are built differently than generic requests.
@return [Nokogiri::XML] the soap as a nokogiri document
# File lib/sepa/soap_builder.rb, line 57 def find_correct_build STANDARD_COMMANDS.include?(@command) ? build_common_request : build_certificate_request end
Loads soap header template to be later populated
@return [Nokogiri::XML] the header as Nokogiri document
# File lib/sepa/soap_builder.rb, line 129 def load_header_template path = File.open("#{SOAP_TEMPLATE_PATH}/header.xml") Nokogiri::XML(path) end
Add needed information to soap header. Mainly security related stuff. The process is as follows:
-
The reference id of the security token is set using {#set_token_id} method
-
Created and expires timestamps are set. Expires is set to be 5 minutes after creation.
-
Timestamp reference id is set with {#set_node_id} method
-
The digest of timestamp node is calculated and set to correct node
-
The reference id of body is set with {#set_node_id}
-
The digest of body is calculated and set to correct node
-
The signature of SignedInfo node is calculated and added to correct node
-
Own signing certificate is formatted (Begin and end certificate removed and linebreaks removed) and embedded in the soap
@todo split into smaller methods
# File lib/sepa/soap_builder.rb, line 167 def process_header set_token_id set_node(@header_template, 'wsu|Created', iso_time) set_node(@header_template, 'wsu|Expires', (Time.now.utc + 300).iso8601) timestamp_id = set_node_id(@header_template, OASIS_UTILITY, 'Timestamp', 0) timestamp_digest = calculate_digest(@header_template, 'wsu|Timestamp') dsig = "dsig|Reference[URI='##{timestamp_id}'] dsig|DigestValue" set_node(@header_template, dsig, timestamp_digest) body_id = set_node_id(@template, ENVELOPE, 'Body', 1) body_digest = calculate_digest(@template, 'env|Body') dsig = "dsig|Reference[URI='##{body_id}'] dsig|DigestValue" set_node(@header_template, dsig, body_digest) signature = calculate_signature(@header_template, 'dsig|SignedInfo') set_node(@header_template, 'dsig|SignatureValue', signature) formatted_cert = format_cert(@own_signing_certificate) set_node(@header_template, 'wsse|BinarySecurityToken', formatted_cert) end
Generates a random request id
@return [String] hexnumeric request id
# File lib/sepa/soap_builder.rb, line 203 def request_id SecureRandom.hex(17) end
# File lib/sepa/soap_builder.rb, line 217 def set_application_request set_node @template, 'bxd|ApplicationRequest', @application_request.to_base64 end
Sets soap body contents. Application request is base64 encoded here.
@return [Nokogiri::XML] the soap with contents added to it
# File lib/sepa/soap_builder.rb, line 85 def set_body_contents set_node @template, "ApplicationRequest", @application_request.to_base64, namespace: cert_ns set_node @template, "SenderId", @customer_id, namespace: cert_ns set_node @template, "RequestId", request_id, namespace: cert_ns set_node @template, "Timestamp", iso_time, namespace: cert_ns @template end
Sets value to a node's content in the given document @param doc [Nokogiri::XML] The document that contains the node @param node [String] The name of the node which value is about to be set @param value [#to_s] The value which will be set to the node
# File lib/sepa/soap_builder.rb, line 138 def set_node(doc, node, value, namespace: nil) if namespace doc.at("xmlns|#{node}", xmlns: namespace).content = value else doc.at(node).content = value end end
Generates a random token id and sets it to correct node
# File lib/sepa/soap_builder.rb, line 193 def set_token_id security_token_id = "token-#{SecureRandom.uuid}" @header_template.at('wsse|BinarySecurityToken')['wsu:Id'] = security_token_id @header_template.at('wsse|Reference')['URI'] = "##{security_token_id}" end