lib/digidoc/client.rb in digidoc_client-0.1.0 vs lib/digidoc/client.rb in digidoc_client-0.1.1
- old
+ new
@@ -6,45 +6,45 @@
require 'mime/types'
require 'digest/sha1'
require 'nokogiri'
module Digidoc
- TargetNamespace = 'http://www.sk.ee/DigiDocService/DigiDocService_2_3.wsdl'
+ TargetNamespace = 'http://www.sk.ee/DigiDocService/DigiDocService_2_3.wsdl'
TestEndpointUrl = 'https://openxades.org:8443/DigiDocService'
-
+
class Client
attr_accessor :session_code, :endpoint_url, :respond_with_nested_struct, :embedded_datafiles
-
- def initialize(endpoint_url = TestEndpointUrl)
+
+ def initialize(endpoint_url = TestEndpointUrl)
self.endpoint_url = endpoint_url || TestEndpointUrl
self.respond_with_nested_struct = true
self.embedded_datafiles = []
end
# Authentication message
def authenticate(*args)
options = args.last || {}
-
+
phone = options.delete(:phone)
personal_code = options.delete(:personal_code)
country_code = options.delete(:country_code) || 'EE'
language = options.delete(:language) || 'EST'
service_name = options.delete(:service_name) || 'Testimine'
message_to_display = options.delete(:message_to_display) || 'Tuvastamine'
messaging_mode = options.delete(:messaging_mode) || 'asynchClientServer'
async_configuration = options.delete(:async_configuration) || 0
return_cert_data = options.key?(:return_cert_data) ? options.delete(:return_cer_data) : true
return_revocation_data = options.key?(:return_revocation_data) ? options.delete(:return_revocation_data) : true
-
+
# SP challenge token
sp_challenge = generate_sp_challenge
phone = ensure_area_code(phone)
self.session_code = nil
-
+
# Make webservice call
response = savon_client.request(:wsdl, 'MobileAuthenticate') do |soap|
- soap.body = {'CountryCode' => country_code, 'PhoneNo' => phone, 'Language' => language, 'ServiceName' => service_name,
+ soap.body = {'CountryCode' => country_code, 'PhoneNo' => phone, 'Language' => language, 'ServiceName' => service_name,
'MessageToDisplay' => message_to_display, 'SPChallenge' => sp_challenge, 'MessagingMode' => messaging_mode,
'AsyncConfiguration' => async_configuration, 'ReturnCertData' => return_cert_data,
'ReturnRevocationData' => return_revocation_data, 'IdCode' => personal_code }
end
@@ -54,17 +54,17 @@
result = response.to_hash[:mobile_authenticate_response]
self.session_code = result[:sesscode]
end
respond_with_hash_or_nested(result)
end
-
+
# Authentication status
def authentication_status(session_code = self.session_code)
response = savon_client.request(:wsdl, 'GetMobileAuthenticateStatus') do |soap|
soap.body = {'Sesscode' => session_code }
- end
-
+ end
+
result = soap_fault?(response) ? response.to_hash[:fault] : response.to_hash[:get_mobile_authenticate_status_response]
respond_with_hash_or_nested(result)
end
# Starts and holds session
@@ -72,95 +72,95 @@
self.session_code = nil
self.embedded_datafiles = []
options = args.last || {}
signed_doc_file = options.delete(:signed_doc_file)
signed_doc_xml = signed_doc_file.read if signed_doc_file
-
+
response = savon_client.request(:wsdl, 'StartSession') do |soap|
soap.body = { 'bHoldSession' => true, 'SigDocXML' => signed_doc_xml}
end
-
+
if soap_fault?(response)
- result = response.to_hash[:fault]
+ result = response.to_hash[:fault]
else
result = response.to_hash[:start_session_response]
self.session_code = result[:sesscode]
end
respond_with_hash_or_nested(result)
end
-
+
# Creates DigiDoc container
def create_signed_doc(*args)
options = args.last || {}
-
+
session_code = options.delete(:session_code) || self.session_code
version = options.delete(:version) || '1.3'
-
+
response = savon_client.request(:wsdl, 'CreateSignedDoc') do |soap|
soap.body = {'Sesscode' => session_code, 'Format' => 'DIGIDOC-XML', 'Version' => version}
end
-
+
result = soap_fault?(response) ? response.to_hash[:fault] : response.to_hash[:create_signed_doc_response]
respond_with_hash_or_nested(result)
end
-
+
def prepare_signature(*args)
options = args.last || {}
-
+
session_code = options.delete(:session_code) || self.session_code
signers_certificate = options.delete(:signers_certificate)
signers_token_id = options.delete(:signers_token_id)
signing_profile = options.delete(:signing_profile)
country_name = options.delete(:country_name) || 'Eesti'
state_or_province = options.delete(:state_or_province)
role = options.delete(:role)
city = options.delete(:city)
- postal_code = options.delete(:postal_code)
-
+ postal_code = options.delete(:postal_code)
+
response = savon_client.request(:wsdl, 'PrepareSignature') do |soap|
- soap.body = {'Sesscode' => session_code, 'SignersCertificate' => signers_certificate,
+ soap.body = {'Sesscode' => session_code, 'SignersCertificate' => signers_certificate,
'SignersTokenId' => signers_token_id, 'Role' => role, 'City' => city,
'State' => state_or_province, 'PostalCode' => postal_code, 'Country' => country_name, 'SigningProfile' => signing_profile }
end
-
+
result = soap_fault?(response) ? response.to_hash[:fault] : response.to_hash[:prepare_signature_response]
- respond_with_hash_or_nested(result)
+ respond_with_hash_or_nested(result)
end
-
+
def finalize_signature(*args)
options = args.last || {}
-
+
session_code = options.delete(:session_code) || self.session_code
signature = options.delete(:signature)
signature_id = options.delete(:signature_id)
-
+
response = savon_client.request(:wsdl, 'FinalizeSignature') do |soap|
soap.body = {'Sesscode' => session_code, 'SignatureValue' => signature, 'SignatureId' => signature_id}
end
-
+
result = soap_fault?(response) ? response.to_hash[:fault] : response.to_hash[:finalize_signature_response]
- respond_with_hash_or_nested(result)
+ respond_with_hash_or_nested(result)
end
def notary(*args)
options = args.last || {}
-
+
session_code = options.delete(:session_code) || self.session_code
signature_id = options.delete(:signature_id)
-
+
response = savon_client.request(:wsdl, 'GetNotary') do |soap|
soap.body = {'Sesscode' => session_code, 'SignatureId' => signature_id}
end
-
+
result = soap_fault?(response) ? response.to_hash[:fault] : response.to_hash[:get_notary_response]
- respond_with_hash_or_nested(result)
+ respond_with_hash_or_nested(result)
end
-
+
# Sign DigiDoc container
def mobile_sign(*args)
options = args.last || {}
-
+
session_code = options.delete(:session_code) || self.session_code
phone = options.delete(:phone)
personal_code = options.delete(:personal_code)
country_code = options.delete(:country_code) || 'EE'
country_name = options.delete(:country_name) || 'Eesti'
@@ -174,130 +174,130 @@
state_or_province = options.delete(:state_or_province)
role = options.delete(:role)
city = options.delete(:city)
postal_code = options.delete(:postal_code)
phone = ensure_area_code(phone)
-
+
response = savon_client.request(:wsdl, 'MobileSign') do |soap|
- soap.body = {'Sesscode' => session_code, 'SignersCountry' => country_code, 'CountryName' => country_name,
- 'SignerPhoneNo' => phone, 'Language' => language, 'ServiceName' => service_name,
+ soap.body = {'Sesscode' => session_code, 'SignersCountry' => country_code, 'CountryName' => country_name,
+ 'SignerPhoneNo' => phone, 'Language' => language, 'ServiceName' => service_name,
'AdditionalDataToBeDisplayed' => message_to_display, 'MessagingMode' => messaging_mode,
'AsyncConfiguration' => async_configuration, 'ReturnDocInfo' => return_doc_info,
'ReturnDocData' => return_doc_data, 'SignerIDCode' => personal_code, 'Role' => role, 'City' => city,
'StateOrProvince' => state_or_province, 'PostalCode' => postal_code }
end
-
+
result = soap_fault?(response) ? response.to_hash[:fault] : response.to_hash[:mobile_sign_response]
respond_with_hash_or_nested(result)
end
-
+
# Get session status info.
def sign_status(*args)
options = args.last || {}
-
+
session_code = options.delete(:session_code) || self.session_code
return_doc_info = options.key?(:return_doc_info) ? options.delete(:return_doc_info) : false
wait_signature = options.key?(:wait_signature) ? options.delete(:wait_signature) : false
-
+
response = savon_client.request(:wsdl, 'GetStatusInfo') do |soap|
soap.body = {'Sesscode' => session_code, 'ReturnDocInfo' => return_doc_info, 'WaitSignature' => wait_signature}
end
-
+
result = soap_fault?(response) ? response.to_hash[:fault] : response.to_hash[:get_status_info_response]
respond_with_hash_or_nested(result)
end
-
+
# Get DigiDoc container status
def signed_doc_info(*args)
- options = args.last || {}
+ options = args.last || {}
session_code = options.delete(:session_code) || self.session_code
-
+
response = savon_client.request(:wsdl, 'GetSignedDocInfo') do |soap|
soap.body = {'Sesscode' => session_code }
end
result = soap_fault?(response) ? response.to_hash[:fault] : response.to_hash[:get_signed_doc_info_response]
respond_with_hash_or_nested(result)
end
-
+
# Get DigiDoc container
def save_signed_doc(*args, &block)
- options = args.last || {}
+ options = args.last || {}
session_code = options.delete(:session_code) || self.session_code
-
+
response = savon_client.request(:wsdl, 'GetSignedDoc') do |soap|
soap.body = {'Sesscode' => session_code }
end
-
+
if soap_fault?(response)
result = respond_with_hash_or_nested(response.to_hash[:fault])
else
- escaped = Crack::XML.parse(response.http.body).to_hash['SOAP_ENV:Envelope']['SOAP_ENV:Body']['d:GetSignedDocResponse']['SignedDocData']
+ escaped = Crack::XML.parse(response.http.body).to_hash['SOAP_ENV:Envelope']['SOAP_ENV:Body']['dig:GetSignedDocResponse']['SignedDocData']
# TODO: is escaping needed? - it removes original escaped & form XML
digidoc_container = escaped#CGI.unescapeHTML(escaped)
-
+
if embedded_datafiles.present?
xmldata = Nokogiri::XML(digidoc_container)
xmldata.root.elements.each { |el| el.replace(embedded_datafiles.shift) if el.name == 'DataFile' }
digidoc_container = xmldata.to_xml
end
-
+
if block_given?
yield digidoc_container
else
digidoc_container
end
- end
+ end
end
-
+
# Closes current session
def close_session(session_code = self.session_code)
response = savon_client.request(:wsdl, 'CloseSession') do |soap|
soap.body = {'Sesscode' => session_code }
end
self.session_code = nil
-
+
result = soap_fault?(response) ? response.to_hash[:fault] : response.to_hash[:close_session_response]
respond_with_hash_or_nested(result)
end
-
+
# Add datafile to DigiDoc container
def add_datafile(file, *args)
options = args.last || {}
-
+
session_code = options.delete(:session_code) || self.session_code
filename = options.delete(:filename) || File.basename(file.path)
mime_type = options[:mime_type] || calc_mime_type(file)
use_hashcode = false #options.key?(:use_hashcode) || true
filename = filename.gsub('/', '-')
-
+
response = savon_client.request(:wsdl, 'AddDataFile') do |soap|
file_content = Base64.encode64(file.read)
# Upload file to webservice
if use_hashcode
# Calculate sha1 from file
datafile = datafile(filename, mime_type, file.size, file_content, embedded_datafiles.size)
self.embedded_datafiles << datafile
hex_sha1 = Digest::SHA1.hexdigest(datafile)
- digest_value = Base64.encode64(hex_sha1.lines.to_a.pack('H*'))
- soap.body = {'Sesscode' => session_code, 'FileName' => filename, 'MimeType' => mime_type, 'ContentType' => 'HASHCODE',
+ digest_value = Base64.encode64(hex_sha1.lines.to_a.pack('H*'))
+ soap.body = {'Sesscode' => session_code, 'FileName' => filename, 'MimeType' => mime_type, 'ContentType' => 'HASHCODE',
'Size' => file.size, 'DigestType' => 'sha1', 'DigestValue' => digest_value}
else
- soap.body = {'Sesscode' => session_code, 'FileName' => filename, 'MimeType' => mime_type, 'ContentType' => 'EMBEDDED_BASE64',
- 'Size' => file.size, 'Content' => file_content}
+ soap.body = {'Sesscode' => session_code, 'FileName' => filename, 'MimeType' => mime_type, 'ContentType' => 'EMBEDDED_BASE64',
+ 'Size' => file.size, 'Content' => file_content}
end
end
-
+
result = soap_fault?(response) ? response.to_hash[:fault] : response.to_hash[:add_data_file_response]
respond_with_hash_or_nested(result)
end
-
- private
+ private
+
def soap_fault?(response)
response.http.body =~ /<*Fault>/
end
-
+
def ensure_area_code(phone)
phone =~ /^\+/ ? phone : "+372#{phone}" unless phone.blank?
end
def savon_client
@@ -306,27 +306,27 @@
wsdl.namespace = TargetNamespace
http.open_timeout = 10
http.auth.ssl.verify_mode = :none # todo: add env dependency
end
end
-
+
def datafile(filename, mime_type, size, content, id)
datafile = "<DataFile ContentType=\"EMBEDDED_BASE64\" Filename=\"#{filename}\" Id=\"D#{id}\" MimeType=\"#{mime_type}\" Size=\"#{size}\">#{content}</DataFile>"
end
-
+
def calc_mime_type(file)
return unless file
MIME::Types.type_for(File.basename(file.path)).first.try(:content_type) || 'text/plain'
end
-
+
def respond_with_hash_or_nested(hash)
if respond_with_nested_struct
NestedOpenStruct.new(hash)
else
hash
end
end
-
+
# Hex ID generator
def generate_unique_hex(codeLength)
validChars = ("A".."F").to_a + ("0".."9").to_a
length = validChars.size
hexCode = ''
\ No newline at end of file