Hide keyboard shortcuts

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 

2import base64 

3import logging 

4import traceback 

5from datetime import date 

6from os.path import basename 

7from typing import Callable, Optional 

8import requests 

9from django.conf import settings 

10from django.template.loader import get_template 

11from django.utils.timezone import now 

12from django.utils.translation import ugettext as _ 

13from zeep.wsse import BinarySignature # type: ignore 

14from jbank.models import WsEdiConnection, WsEdiSoapCall 

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

16 

17 

18logger = logging.getLogger(__name__) 

19 

20 

21def 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 

66def 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 

107def 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