]> git.ipfire.org Git - thirdparty/python-fints.git/commitdiff
Pool Exceptions in one module
authorHenryk Plötz <henryk@ploetzli.ch>
Wed, 29 Aug 2018 15:03:18 +0000 (17:03 +0200)
committerRaphael Michel <mail@raphaelmichel.de>
Mon, 3 Dec 2018 18:34:29 +0000 (19:34 +0100)
Raise Exceptions during executions, generally prepare for integration with external library users
Be over-cautious with PIN errors: On first sign of wrong PIN, block the PIN from usage (raise Exception when trying to use the PIN). Library user will need to create a new FinTS3PinTanClient instance.

fints/client.py
fints/connection.py
fints/dialog.py
fints/exceptions.py [new file with mode: 0644]
fints/utils.py

index d34d9f9aa97354476bd9ac96e6f74c7a63091efd..a6e37af3ee2b938aa65ca39326450b5a76b82228 100644 (file)
@@ -38,6 +38,7 @@ from .segments.journal import HKPRO3, HKPRO4
 from .types import SegmentSequence
 from .utils import MT535_Miniparser, Password, mt940_to_array, compress_datablob, decompress_datablob, SubclassesMixin
 from .parser import FinTS3Serializer
+from .exceptions import *
 
 logger = logging.getLogger(__name__)
 
@@ -108,10 +109,10 @@ class FinTS3Client:
     def _ensure_system_id(self):
         raise NotImplemented()
 
-    def _process_response(self, segment, response):
+    def _process_response(self, dialog, segment, response):
         pass
 
-    def process_response_message(self, message: FinTSInstituteMessage, internal_send=True):
+    def process_response_message(self, dialog, message: FinTSInstituteMessage, internal_send=True):
         bpa = message.find_segment_first(HIBPA3)
         if bpa:
             self.bpa = bpa
@@ -137,7 +138,7 @@ class FinTS3Client:
 
                     self._call_callbacks(None, response)
 
-                self._process_response(None, response)
+                self._process_response(dialog, None, response)
 
         for seg in message.find_segments(HIRMS2):
             for response in seg.responses:
@@ -148,7 +149,7 @@ class FinTS3Client:
 
                     self._call_callbacks(segment, response)
 
-                self._process_response(segment, response)
+                self._process_response(dialog, segment, response)
 
     def _send_with_possible_retry(self, dialog, command_seg, resume_func):
         response = dialog._send(command_seg)
@@ -158,6 +159,7 @@ class FinTS3Client:
         if self._standing_dialog:
             raise Exception("Cannot double __enter__() {}".format(self))
         self._standing_dialog = self._get_dialog()
+        self._standing_dialog.lazy_init = True  # FIXME Inelegant
         self._standing_dialog.__enter__()
 
     def __exit__(self, exc_type, exc_value, traceback):
@@ -931,7 +933,7 @@ class FinTS3PinTanClient(FinTS3Client):
             resume_func = getattr(self, challenge.resume_method)
             return resume_func(challenge.command_seg, response)
 
-    def _process_response(self, segment, response):
+    def _process_response(self, dialog, segment, response):
         if response.code == '3920':
             self.allowed_security_functions = list(response.parameters)
             if self.selected_security_function is None or not self.selected_security_function in self.allowed_security_functions:
@@ -952,6 +954,16 @@ class FinTS3PinTanClient(FinTS3Client):
                     # Fall back to onestep
                     self.set_tan_mechanism('999')
 
+        if (not dialog.open and response.code.startswith('9')) or response.code in ('9340', '9910', '9930', '9931', '9942'):
+            # Assume all 9xxx errors in a not-yet-open dialog refer to the PIN or authentication
+            # During a dialog also listen for the following codes which may explicitly indicate an
+            # incorrect pin: 9340, 9910, 9930, 9931, 9942
+            # Fail-safe block all further attempts with this PIN
+            if self.pin:
+                self.pin.block()
+            raise FinTSClientPINError("Error during dialog initialization, PIN wrong?")
+
+
     def get_tan_mechanisms(self):
         """
         Get the available TAN mechanisms.
index 20cba038eac720fbbbe9923fc7ce8b6d373d7a8b..23349fa7528f0ebd65a2585a670af939f3fa4fdf 100644 (file)
@@ -7,12 +7,10 @@ from fints.parser import FinTS3Parser
 from fints.utils import Password
 
 from .message import FinTSInstituteMessage, FinTSMessage
+from .exceptions import *
 
 logger = logging.getLogger(__name__)
 
-class FinTSConnectionError(Exception):
-    pass
-
 
 class FinTSHTTPSConnection:
     def __init__(self, url):
index 6a020f1e9531c12df4efe380d449a90797eea47e..163d74b469a954ef4147c48e1817eb221e9ee726 100644 (file)
@@ -12,16 +12,14 @@ from .segments.auth import HKIDN2, HKVVB3
 from .segments.dialog import HKEND1
 from .segments.message import HNHBK3, HNHBS1
 from .utils import compress_datablob, decompress_datablob
+from .connection import FinTSConnectionError
+from .exceptions import *
 
 logger = logging.getLogger(__name__)
 
 DIALOGUE_ID_UNASSIGNED = '0'
 DATA_BLOB_MAGIC = b'python-fints_DIALOG_DATABLOB'
 
-class FinTSDialogError(Exception):
-    pass
-
-
 class FinTSDialog:
     def __init__(self, client=None, lazy_init=False, enc_mechanism=None, auth_mechanisms=[]):
         self.client = client
@@ -51,7 +49,7 @@ class FinTSDialog:
 
     def init(self, *extra_segments):
         if self.paused:
-            raise Error("Cannot init() a paused dialog")
+            raise FinTSDialogStateError("Cannot init() a paused dialog")
 
         if self.need_init and not self.open:
             segments = [
@@ -77,15 +75,18 @@ class FinTSDialog:
                 retval = self.send(*segments, internal_send=True)
                 self.need_init = False
                 return retval
-            except:
+            except Exception as e:
                 self.open = False
-                raise
+                if isinstance(e, (FinTSConnectionError, FinTSClientError)):
+                    raise
+                else:
+                    raise FinTSDialogInitError("Couldn't establish dialog with bank, Authentication data wrong?") from e
             finally:
                 self.lazy_init = False
 
     def end(self):
         if self.paused:
-            raise Error("Cannot end() on a paused dialog")
+            raise FinTSDialogStateError("Cannot end() on a paused dialog")
 
         if self.open:
             response = self.send(HKEND1(self.dialogue_id), internal_send=True)
@@ -95,14 +96,14 @@ class FinTSDialog:
         internal_send = kwargs.pop('internal_send', False)
 
         if self.paused:
-            raise Error("Cannot send() on a paused dialog")
+            raise FinTSDialogStateError("Cannot send() on a paused dialog")
 
         if not self.open:
             if self.lazy_init and self.need_init:
                 self.init()
 
         if not self.open:
-            raise Exception("Cannot send on dialog that is not open")
+            raise FinTSDialogStateError("Cannot send on dialog that is not open")
 
         message = self.new_customer_message()
         for s in segments:
@@ -129,16 +130,16 @@ class FinTSDialog:
         if self.dialogue_id == DIALOGUE_ID_UNASSIGNED:
             seg = response.find_segment_first(HNHBK3)
             if not seg:
-                raise ValueError('Could not find dialogue_id')
+                raise FinTSDialogError('Could not find dialogue_id')
             self.dialogue_id = seg.dialogue_id
 
-        self.client.process_response_message(response, internal_send=internal_send)
+        self.client.process_response_message(self, response, internal_send=internal_send)
 
         return response
 
     def new_customer_message(self):
         if self.paused:
-            raise Error("Cannot call new_customer_message() on a paused dialog")
+            raise FinTSDialogStateError("Cannot call new_customer_message() on a paused dialog")
 
         message = FinTSCustomerMessage(self)
         message += HNHBK3(0, 300, self.dialogue_id, self.next_message_number[message.DIRECTION])
@@ -150,7 +151,7 @@ class FinTSDialog:
 
     def finish_message(self, message):
         if self.paused:
-            raise Error("Cannot call finish_message() on a paused dialog")
+            raise FinTSDialogStateError("Cannot call finish_message() on a paused dialog")
 
         # Create signature(s) in reverse order: from inner to outer
         for auth_mech in reversed(self.auth_mechanisms):
@@ -166,7 +167,7 @@ class FinTSDialog:
     def pause(self):
         # FIXME Document, test
         if self.paused:
-            raise Error("Cannot pause a paused dialog")
+            raise FinTSDialogStateError("Cannot pause a paused dialog")
 
         external_dialog = self
         external_client = self.client
diff --git a/fints/exceptions.py b/fints/exceptions.py
new file mode 100644 (file)
index 0000000..3fb5e0a
--- /dev/null
@@ -0,0 +1,21 @@
+class FinTSError(Exception):
+    pass
+
+class FinTSClientError(FinTSError):
+    pass
+
+class FinTSClientPINError(FinTSClientError):
+    pass
+
+class FinTSDialogError(FinTSError):
+    pass
+
+class FinTSDialogStateError(FinTSDialogError):
+    pass
+
+class FinTSDialogInitError(FinTSDialogError):
+    pass
+
+class FinTSConnectionError(FinTSError):
+    pass
+
index f4afe2300b775dc186f6d8f7251f3f5218db6e86..4d6b35b84c0f35921ba3d0b2d1859bdc498d7c9d 100644 (file)
@@ -264,6 +264,7 @@ class Password(str):
 
     def __init__(self, value):
         self.value = value
+        self.blocked = False
 
     @classmethod
     @contextmanager
@@ -274,7 +275,12 @@ class Password(str):
         finally:
             cls.protected = False
 
+    def block(self):
+        self.blocked = True
+
     def __str__(self):
+        if self.blocked and not self.protected:
+            raise Exception("Refusing to use PIN after block")
         return '***' if self.protected else str(self.value)
 
     def __repr__(self):