Coverage for jbank/wsedi.py: 0%

# Coverage for jbank/wsedi.py : 0%

## 120 statements

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

1# pylint: disable=logging-format-interpolation,logging-not-lazy,too-many-arguments,too-many-locals,too-many-statements

2 import base64

3 import logging

4 import traceback

5 from datetime import date

6 from os.path import basename

7 from typing import Callable, Optional

8 import requests

9 from django.conf import settings

10 from django.template.loader import get_template

11 from django.utils.timezone import now

12 from django.utils.translation import ugettext as _

13 from zeep.wsse import BinarySignature # type: ignore

14 from jbank.models import WsEdiConnection, WsEdiSoapCall

15 from lxml import etree # type: ignore # pytype: disable=import-error

16

17

18 logger = logging.getLogger(__name__)

19

20

21 def wsedi_get(command: str, file_type: str, status: str, file_reference: str = '', verbose: bool = False) -> requests.Response:

22 """

23 Download Finnish bank files. Assumes WS-EDI API parameter compatible HTTP REST API end-point.

24 Uses project settings WSEDI_URL and WSEDI_TOKEN.

25 :param command: Command, e.g. DownloadFileList or DownloadFile

26 :param file_type: File type, e.g. TO or SVM

27 :param status: Status, e.g. DLD or NEW

28 :param file_reference: File reference (if command is DownloadFile)

29 :param verbose: Debug output

30 :return: requests.Response

31 """

32 url = settings.WSEDI_URL + '?command={command}'.format(command=command)

33 if file_reference:

34 url += '&file-reference=' + file_reference

35 if file_type:

36 url += '&file-type=' + file_type

37 if status:

38 url += '&status=' + status

39 headers = {

40 'Content-Type': 'application/json',

41 'Authorization': 'Token ' + settings.WSEDI_TOKEN,

42 }

43 res = requests.get(url, headers=headers)

44 if res.status_code >= 300:

45 logger.error("wsedi_get(command={}, file_type={}, status={}, file_reference={}) response HTTP {}:\n".format(

46 command, file_type, status, file_reference, res.status_code) + res.text)

47 elif verbose:

48 logger.info("wsedi_get(command={}, file_type={}, status={}, file_reference={}) response HTTP {}:\n".format(

49 command, file_type, status, file_reference, res.status_code) + res.text)

50

51 if res.status_code >= 300:

52 raise Exception("WS-EDI {} HTTP {}".format(command, res.status_code))

53 return res

54

55

56 def wsedi_upload_file(file_content: str, file_type: str, file_name: str, verbose: bool = False) -> requests.Response:

57 """

58 Upload Finnish bank file. Assumes WS-EDI API parameter compatible HTTP REST API end-point.

59 Uses project settings WSEDI_URL and WSEDI_TOKEN.

60 :param file_content: File content

61 :param file_type: File type, e.g. pain.001.001.03

62 :param file_name: File (base) name

63 :param verbose: Debug output

64 :return: requests.Response

65 """

66 command = 'UploadFile'

67 url = settings.WSEDI_URL

68 data = {

69 'command': command,

70 'file-type': file_type,

71 'file-name': basename(file_name),

72 'file-content': base64.b64encode(file_content.encode('utf8')).decode('ascii'),

73 }

74 headers = {

75 'Content-Type': 'application/x-www-form-urlencoded',

76 'Authorization': 'Token ' + settings.WSEDI_TOKEN,

77 }

78 res = requests.post(url, data=data, headers=headers)

79 if res.status_code >= 300:

80 logger.error("wsedi_upload_file(command={}, file_type={}, file_name={}) response HTTP {}:\n".format(

81 command, file_type, file_name, res.status_code) + res.text)

82 raise Exception("WS-EDI {} HTTP {}".format(command, res.status_code))

83 if verbose:

84 logger.info("wsedi_upload_file(command={}, file_type={}, file_name={}) response HTTP {}:\n".format(

85 command, file_type, file_name, res.status_code) + res.text)

86 return res

87

88

89 def wsedi_execute(ws: WsEdiConnection, command: str, file_type: str = '', status: str = '', file_reference: str = '', # noqa

90 file_content: str = '', start_date: Optional[date] = None, end_date: Optional[date] = None,

91 verbose: bool = False, cls: Callable = WsEdiSoapCall, **kwargs) -> bytes:

92 """

93 :param ws:

94 :param command:

95 :param file_type:

96 :param status:

97 :param file_reference:

98 :param file_content:

99 :param start_date:

100 :param end_date:

101 :param verbose:

102 :param cls:

103 :return: str

104 """

105 if ws and not ws.enabled:

106 raise Exception(_('ws.edi.connection.not.enabled').format(ws=ws))

107

108 soap_call = cls(connection=ws, command=command, **kwargs)

109 soap_call.full_clean()

110 soap_call.save()

111 call_str = 'WsEdiSoapCall({})'.format(soap_call.id)

112 try:

113 content = ''

114 if file_content:

115 content = base64.b64encode(file_content.encode()).decode('ascii')

116

117 app = ws.get_application_request(command, file_type=file_type, status=status, file_reference=file_reference, content=content,

118 start_date=start_date, end_date=end_date)

119 if verbose:

120 logger.info('------------------------------------------------------ {} app\n{}'.format(call_str, app.decode()))

121 debug_output = command in ws.debug_command_list or 'ALL' in ws.debug_command_list

122 if debug_output:

123 with open(soap_call.debug_request_full_path, 'wb') as fp:

124 fp.write(app)

125

126 signed_app = ws.sign_application_request(app)

127 if verbose:

128 logger.info('------------------------------------------------------ {} signed_app\n{}'.format(call_str, signed_app.decode()))

129

130 if ws.bank_encryption_cert_file:

131 enc_app = ws.encrypt_application_request(signed_app)

132 if verbose:

133 logger.info('------------------------------------------------------ {} enc_app\n{}'.format(call_str, enc_app.decode()))

134 else:

135 enc_app = signed_app

136 if verbose:

137 logger.info('------------------------------------------------------ '

138 '{} enc_app\n(no bank_encryption_cert_file, not encrypting)'.format(call_str))

139

140 b64_app = ws.encode_application_request(enc_app)

141 if verbose:

142 logger.info('------------------------------------------------------ {} b64_app\n{}'.format(call_str, b64_app.decode()))

143

144 soap_body = get_template('jbank/soap_template.xml').render({

145 'soap_call': soap_call,

146 'payload': b64_app.decode(),

147 })

148 if verbose:

149 logger.info('------------------------------------------------------ {} soap_body\n{}'.format(call_str, soap_body))

150

151 body_bytes = soap_body.encode()

152 envelope = etree.fromstring(body_bytes)

153 binary_signature = BinarySignature(ws.signing_key_full_path, ws.signing_cert_full_path)

154 soap_headers: dict = {}

155 envelope, soap_headers = binary_signature.apply(envelope, soap_headers)

156 signed_body_bytes = etree.tostring(envelope)

157 if verbose:

158 logger.info('------------------------------------------------------ {} signed_body_bytes\n{}'.format(call_str, signed_body_bytes))

159

160 http_headers = {

161 'Connection': 'Close',

162 'Content-Type': 'text/xml',

163 'Method': 'POST',

164 'SOAPAction': '',

165 'User-Agent': 'Kajala WS',

166 }

167 if verbose:

168 logger.info('HTTP POST {}'.format(ws.soap_endpoint))

169 res = requests.post(ws.soap_endpoint, data=signed_body_bytes, headers=http_headers)

170 if verbose:

171 logger.info('------------------------------------------------------ {} HTTP response {}\n{}'.format(call_str, res.status_code, res.text))

172 if res.status_code >= 300:

173 logger.error('------------------------------------------------------ {} HTTP response {}\n{}'.format(call_str, res.status_code, res.text))

174 raise Exception("WS-EDI {} HTTP {}".format(command, res.status_code))

175

176 envelope = etree.fromstring(res.content)

177 app_res_el = envelope.find('.//{http://model.bxd.fi}ApplicationResponse')

178 if app_res_el is None:

179 logger.error('------------------------------------------------------ {} HTTP response {}\n{}'.format(call_str, res.status_code, res.text))

180 raise Exception("WS-EDI {} failed, missing ApplicationResponse".format(command))

181 app_res_enc = ws.decode_application_response(app_res_el.text.encode())

182 if verbose:

183 logger.info('------------------------------------------------------ {} app_res_enc\n{}'.format(call_str, app_res_enc.decode()))

184

185 if ws.encryption_key_file:

186 app_res = ws.decrypt_application_response(app_res_enc)

187 if verbose:

188 logger.info('------------------------------------------------------ {} app_res\n{}'.format(call_str, app_res.decode()))

189 else:

190 app_res = app_res_enc

191 if verbose:

192 logger.info('------------------------------------------------------ '

193 '{} app_res\n(no encryption_key_file, assuming decrypted content)'.format(call_str))

194

195 soap_call.executed = now()

196 soap_call.save(update_fields=['executed'])

197

198 if debug_output:

199 with open(soap_call.debug_response_full_path, 'wb') as fp:

200 fp.write(app_res)

201

202 return app_res

203 except Exception:

204 soap_call.error = traceback.format_exc()

205 soap_call.save(update_fields=['error'])

206 raise