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(

22 command: str, file_type: str, status: str, file_reference: str = "", verbose: bool = False

23) -> requests.Response:

24 """

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

26 Uses project settings WSEDI_URL and WSEDI_TOKEN.

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

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

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

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

31 :param verbose: Debug output

32 :return: requests.Response

33 """

34 url = settings.WSEDI_URL + "?command={command}".format(command=command)

35 if file_reference:

36 url += "&file-reference=" + file_reference

37 if file_type:

38 url += "&file-type=" + file_type

39 if status:

40 url += "&status=" + status

41 headers = {

42 "Content-Type": "application/json",

43 "Authorization": "Token " + settings.WSEDI_TOKEN,

44 }

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

46 if res.status_code >= 300:

47 logger.error(

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

49 command, file_type, status, file_reference, res.status_code

50 )

51 + res.text

52 )

53 elif verbose:

54 logger.info(

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

56 command, file_type, status, file_reference, res.status_code

57 )

58 + res.text

59 )

60

61 if res.status_code >= 300:

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

63 return res

64

65

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

67 """

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

69 Uses project settings WSEDI_URL and WSEDI_TOKEN.

70 :param file_content: File content

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

72 :param file_name: File (base) name

73 :param verbose: Debug output

74 :return: requests.Response

75 """

76 command = "UploadFile"

77 url = settings.WSEDI_URL

78 data = {

79 "command": command,

80 "file-type": file_type,

81 "file-name": basename(file_name),

82 "file-content": base64.b64encode(file_content.encode("utf8")).decode("ascii"),

83 }

84 headers = {

85 "Content-Type": "application/x-www-form-urlencoded",

86 "Authorization": "Token " + settings.WSEDI_TOKEN,

87 }

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

89 if res.status_code >= 300:

90 logger.error(

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

92 command, file_type, file_name, res.status_code

93 )

94 + res.text

95 )

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

97 if verbose:

98 logger.info(

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

100 command, file_type, file_name, res.status_code

101 )

102 + res.text

103 )

104 return res

105

106

107 def wsedi_execute( # noqa

108 ws: WsEdiConnection,

109 command: str,

110 file_type: str = "",

111 status: str = "",

112 file_reference: str = "", # noqa

113 file_content: str = "",

114 start_date: Optional[date] = None,

115 end_date: Optional[date] = None,

116 verbose: bool = False,

117 cls: Callable = WsEdiSoapCall,

118 **kwargs

119) -> bytes:

120 """

121 :param ws:

122 :param command:

123 :param file_type:

124 :param status:

125 :param file_reference:

126 :param file_content:

127 :param start_date:

128 :param end_date:

129 :param verbose:

130 :param cls:

131 :return: bytes

132 """

133 if ws and not ws.enabled:

134 raise Exception(_("ws.edi.connection.not.enabled").format(ws=ws))

135

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

137 soap_call.full_clean()

138 soap_call.save()

139 call_str = "WsEdiSoapCall({})".format(soap_call.id)

140 try:

141 content = ""

142 if file_content:

143 content = base64.b64encode(file_content.encode()).decode("ascii")

144

145 app = ws.get_application_request(

146 command,

147 file_type=file_type,

148 status=status,

149 file_reference=file_reference,

150 content=content,

151 start_date=start_date,

152 end_date=end_date,

153 )

154 if verbose:

155 logger.info(

156 "------------------------------------------------------ {} app\n{}".format(call_str, app.decode())

157 )

158 debug_output = command in ws.debug_command_list or "ALL" in ws.debug_command_list

159 if debug_output:

160 with open(soap_call.debug_request_full_path, "wb") as fp:

161 fp.write(app)

162

163 signed_app = ws.sign_application_request(app)

164 if verbose:

165 logger.info(

166 "------------------------------------------------------ {} signed_app\n{}".format(

167 call_str, signed_app.decode()

168 )

169 )

170

171 if ws.bank_encryption_cert_file:

172 enc_app = ws.encrypt_application_request(signed_app)

173 if verbose:

174 logger.info(

175 "------------------------------------------------------ {} enc_app\n{}".format(

176 call_str, enc_app.decode()

177 )

178 )

179 else:

180 enc_app = signed_app

181 if verbose:

182 logger.info(

183 "------------------------------------------------------ "

184 "{} enc_app\n(no bank_encryption_cert_file, not encrypting)".format(call_str)

185 )

186

187 b64_app = ws.encode_application_request(enc_app)

188 if verbose:

189 logger.info(

190 "------------------------------------------------------ {} b64_app\n{}".format(

191 call_str, b64_app.decode()

192 )

193 )

194

195 soap_body = get_template("jbank/soap_template.xml").render(

196 {

197 "soap_call": soap_call,

198 "payload": b64_app.decode(),

199 }

200 )

201 if verbose:

202 logger.info(

203 "------------------------------------------------------ {} soap_body\n{}".format(call_str, soap_body)

204 )

205

206 body_bytes = soap_body.encode()

207 envelope = etree.fromstring(body_bytes)

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

209 soap_headers: dict = {}

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

211 signed_body_bytes = etree.tostring(envelope)

212 if verbose:

213 logger.info(

214 "------------------------------------------------------ {} signed_body_bytes\n{}".format(

215 call_str, signed_body_bytes

216 )

217 )

218

219 http_headers = {

220 "Connection": "Close",

221 "Content-Type": "text/xml",

222 "Method": "POST",

223 "SOAPAction": "",

224 "User-Agent": "Kajala WS",

225 }

226 if verbose:

227 logger.info("HTTP POST {}".format(ws.soap_endpoint))

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

229 if verbose:

230 logger.info(

231 "------------------------------------------------------ {} HTTP response {}\n{}".format(

232 call_str, res.status_code, res.text

233 )

234 )

235 if res.status_code >= 300:

236 logger.error(

237 "------------------------------------------------------ {} HTTP response {}\n{}".format(

238 call_str, res.status_code, res.text

239 )

240 )

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

242

243 envelope = etree.fromstring(res.content)

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

245 if app_res_el is None:

246 logger.error(

247 "------------------------------------------------------ {} HTTP response {}\n{}".format(

248 call_str, res.status_code, res.text

249 )

250 )

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

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

253 if verbose:

254 logger.info(

255 "------------------------------------------------------ {} app_res_enc\n{}".format(

256 call_str, app_res_enc.decode()

257 )

258 )

259

260 if ws.encryption_key_file:

261 app_res = ws.decrypt_application_response(app_res_enc)

262 if verbose:

263 logger.info(

264 "------------------------------------------------------ {} app_res\n{}".format(

265 call_str, app_res.decode()

266 )

267 )

268 else:

269 app_res = app_res_enc

270 if verbose:

271 logger.info(

272 "------------------------------------------------------ "

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

274 )

275

276 soap_call.executed = now()

277 soap_call.save(update_fields=["executed"])

278

279 if debug_output:

280 with open(soap_call.debug_response_full_path, "wb") as fp:

281 fp.write(app_res)

282

283 return app_res

284 except Exception:

285 soap_call.error = traceback.format_exc()

286 soap_call.save(update_fields=["error"])

287 raise