From: Raphael Michel Date: Thu, 29 Nov 2018 17:29:55 +0000 (+0100) Subject: Unify code style X-Git-Tag: v2.0.0~1^2~25 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=292306c6ce9209c8886333232612af93c3720208;p=thirdparty%2Fpython-fints.git Unify code style --- diff --git a/fints/client.py b/fints/client.py index c285f7a..374907c 100644 --- a/fints/client.py +++ b/fints/client.py @@ -48,6 +48,7 @@ SYSTEM_ID_UNASSIGNED = '0' DATA_BLOB_MAGIC = b'python-fints_DATABLOB' DATA_BLOB_MAGIC_RETRY = b'python-fints_RETRY_DATABLOB' + class FinTSOperations(Enum): """This enum is used as keys in the 'supported_operations' member of the get_information() response. @@ -74,6 +75,7 @@ class FinTSOperations(Enum): GET_SEPA_STANDING_DEBITS_SINGLE = ("HKDDB", ) SEPA_STANDING_DEBIT_SINGLE_DELETE = ("HKDDL", ) + class NeedRetryResponse(SubclassesMixin, metaclass=ABCMeta): """Base class for Responses that need the operation to be externally retried. @@ -102,6 +104,7 @@ class NeedRetryResponse(SubclassesMixin, metaclass=ABCMeta): raise Exception("Invalid data blob data or version") + class ResponseStatus(Enum): "Error status of the response" @@ -110,12 +113,14 @@ class ResponseStatus(Enum): WARNING = 2 #: Response indicates a Warning ERROR = 3 #: Response indicates an Error + _RESPONSE_STATUS_MAPPING = { '0': ResponseStatus.SUCCESS, '3': ResponseStatus.WARNING, '9': ResponseStatus.ERROR, } + class TransactionResponse: """Result of a FinTS operation. @@ -142,8 +147,9 @@ class TransactionResponse: def __repr__(self): return "<{o.__class__.__name__}(status={o.status!r}, responses={o.responses!r}, data={o.data!r})>".format(o=self) + class FinTS3Client: - def __init__(self, bank_identifier, user_id, customer_id=None, set_data:bytes=None): + def __init__(self, bank_identifier, user_id, customer_id=None, set_data: bytes=None): self.accounts = [] if isinstance(bank_identifier, BankIdentifier): self.bank_identifier = bank_identifier @@ -389,7 +395,6 @@ class FinTS3Client: retval['accounts'].append(acc) return retval - def get_sepa_accounts(self): """ Returns a list of SEPA accounts @@ -457,7 +462,6 @@ class FinTS3Client: else: return version_map.get(max_version.header.version) - def get_transactions(self, account: SEPAAccount, start_date: datetime.date = None, end_date: datetime.date = None): """ Fetches the list of transactions of a bank account in a certain timeframe. @@ -617,8 +621,8 @@ class FinTS3Client: return responses def simple_sepa_transfer(self, account: SEPAAccount, iban: str, bic: str, - recipient_name: str, amount: Decimal, account_name: str, reason: str, - endtoend_id='NOTPROVIDED'): + recipient_name: str, amount: Decimal, account_name: str, reason: str, + endtoend_id='NOTPROVIDED'): """ Simple SEPA transfer. @@ -654,8 +658,8 @@ class FinTS3Client: return self.sepa_transfer(account, xml) def sepa_transfer(self, account: SEPAAccount, pain_message: bytes, multiple=False, - control_sum=None, currency='EUR', book_as_single=False, - pain_descriptor='urn:iso:std:iso:20022:tech:xsd:pain.001.001.03'): + control_sum=None, currency='EUR', book_as_single=False, + pain_descriptor='urn:iso:std:iso:20022:tech:xsd:pain.001.001.03'): """ Custom SEPA transfer. @@ -712,8 +716,8 @@ class FinTS3Client: return retval def sepa_debit(self, account: SEPAAccount, pain_message: str, multiple=False, cor1=False, - control_sum=None, currency='EUR', book_as_single=False, - pain_descriptor='urn:iso:std:iso:20022:tech:xsd:pain.008.003.01'): + control_sum=None, currency='EUR', book_as_single=False, + pain_descriptor='urn:iso:std:iso:20022:tech:xsd:pain.008.003.01'): """ Custom SEPA debit. @@ -861,12 +865,13 @@ class FinTS3Client: yield self self._standing_dialog = None + class NeedTANResponse(NeedRetryResponse): challenge_raw = None #: Raw challenge as received by the bank challenge = None #: Textual challenge to be displayed to the user challenge_html = None #: HTML-safe challenge text, possibly with formatting challenge_hhduc = None #: HHD_UC challenge to be transmitted to the TAN generator - challenge_matrix = None #: Matrix code challenge: tuple(mime_type, data) + challenge_matrix = None #: Matrix code challenge: tuple(mime_type, data) def __init__(self, command_seg, tan_request, resume_method=None, tan_request_structured=False): self.command_seg = command_seg @@ -955,6 +960,7 @@ IMPLEMENTED_HKTAN_VERSIONS = { 5: HKTAN5, } + class FinTS3PinTanClient(FinTS3Client): def __init__(self, bank_identifier, user_id, pin, server, customer_id=None, *args, **kwargs): @@ -981,7 +987,8 @@ class FinTS3PinTanClient(FinTS3Client): self.pin, )] - return FinTSDialog(self, + return FinTSDialog( + self, lazy_init=lazy_init, enc_mechanism=enc, auth_mechanisms=auth, @@ -1134,7 +1141,6 @@ class FinTS3PinTanClient(FinTS3Client): self.pin.block() raise FinTSClientPINError("Error during dialog initialization, PIN wrong?") - def get_tan_mechanisms(self): """ Get the available TAN mechanisms. diff --git a/fints/dialog.py b/fints/dialog.py index 4cc317d..b99b34b 100644 --- a/fints/dialog.py +++ b/fints/dialog.py @@ -16,6 +16,7 @@ logger = logging.getLogger(__name__) DIALOGUE_ID_UNASSIGNED = '0' DATA_BLOB_MAGIC = b'python-fints_DIALOG_DATABLOB' + class FinTSDialog: def __init__(self, client=None, lazy_init=False, enc_mechanism=None, auth_mechanisms=[]): self.client = client diff --git a/fints/exceptions.py b/fints/exceptions.py index ee9f2ee..730456f 100644 --- a/fints/exceptions.py +++ b/fints/exceptions.py @@ -1,21 +1,27 @@ 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 diff --git a/fints/fields.py b/fints/fields.py index 7519c91..07ac86e 100644 --- a/fints/fields.py +++ b/fints/fields.py @@ -11,6 +11,7 @@ from fints.utils import ( class DataElementField(DocTypeMixin, TypedField): pass + class ContainerField(TypedField): def _check_value(self, value): if self.type: @@ -25,6 +26,7 @@ class ContainerField(TypedField): class DataElementGroupField(DocTypeMixin, ContainerField): pass + class GenericField(FieldRenderFormatStringMixin, DataElementField): type = None _FORMAT_STRING = "{}" @@ -33,6 +35,7 @@ class GenericField(FieldRenderFormatStringMixin, DataElementField): warnings.warn("Generic field used for type {!r} value {!r}".format(self.type, value)) return value + class GenericGroupField(DataElementGroupField): type = None @@ -47,6 +50,7 @@ class GenericGroupField(DataElementGroupField): warnings.warn("Generic field used for type {!r} value {!r}".format(self.type, value)) return value + class TextField(FieldRenderFormatStringMixin, DataElementField): type = 'txt' _DOC_TYPE = str @@ -54,12 +58,15 @@ class TextField(FieldRenderFormatStringMixin, DataElementField): def _parse_value(self, value): return str(value) + class AlphanumericField(TextField): type = 'an' - + + class DTAUSField(DataElementField): type = 'dta' + class NumericField(FieldRenderFormatStringMixin, DataElementField): type = 'num' _DOC_TYPE = int @@ -71,6 +78,7 @@ class NumericField(FieldRenderFormatStringMixin, DataElementField): raise ValueError("Leading zeroes not allowed for value of type 'num': {!r}".format(value)) return int(_value, 10) + class ZeroPaddedNumericField(NumericField): type = '' _DOC_TYPE = int @@ -88,6 +96,7 @@ class ZeroPaddedNumericField(NumericField): _value = str(value) return int(_value, 10) + class DigitsField(FieldRenderFormatStringMixin, DataElementField): type = 'dig' _DOC_TYPE = str @@ -99,6 +108,7 @@ class DigitsField(FieldRenderFormatStringMixin, DataElementField): raise TypeError("Only digits allowed for value of type 'dig': {!r}".format(value)) return _value + class FloatField(DataElementField): type = 'float' _DOC_TYPE = float @@ -121,11 +131,13 @@ class FloatField(DataElementField): self._check_value_length(retval) return retval + class AmountField(FixedLengthMixin, FloatField): type = 'wrt' _FIXED_LENGTH = [None, None, 15] # FIXME Needs test + class BinaryField(DataElementField): type = 'bin' _DOC_TYPE = bytes @@ -138,11 +150,13 @@ class BinaryField(DataElementField): def _parse_value(self, value): return bytes(value) + class IDField(FixedLengthMixin, AlphanumericField): type = 'id' _DOC_TYPE = str _FIXED_LENGTH = [None, None, 30] + class BooleanField(FixedLengthMixin, AlphanumericField): type = 'jn' _DOC_TYPE = bool @@ -161,6 +175,7 @@ class BooleanField(FixedLengthMixin, AlphanumericField): else: raise ValueError("Invalid value {!r} for BooleanField".format(value)) + class CodeFieldMixin: # FIXME Need tests @@ -196,23 +211,28 @@ class CodeFieldMixin: retval = retval + addendum return retval + class CodeField(CodeFieldMixin, AlphanumericField): type = 'code' _DOC_TYPE = str + class IntCodeField(CodeFieldMixin, NumericField): type = '' _DOC_TYPE = int _FORMAT_STRING = "{}" + class CountryField(FixedLengthMixin, DigitsField): type = 'ctr' _FIXED_LENGTH = [3] + class CurrencyField(FixedLengthMixin, AlphanumericField): type = 'cur' _FIXED_LENGTH = [3] + class DateField(FixedLengthMixin, NumericField): type = 'dat' # FIXME Need test _DOC_TYPE = datetime.date @@ -230,6 +250,7 @@ class DateField(FixedLengthMixin, NumericField): val = int(val) return super()._render_value(val) + class TimeField(FixedLengthMixin, DigitsField): type = 'tim' # FIXME Need test _DOC_TYPE = datetime.time @@ -245,6 +266,7 @@ class TimeField(FixedLengthMixin, DigitsField): val = "{:02d}{:02d}{:02d}".format(value.hour, value.minute, value.second) return super()._render_value(val) + class PasswordField(AlphanumericField): type = '' _DOC_TYPE = Password @@ -255,6 +277,7 @@ class PasswordField(AlphanumericField): def _render_value(self, value): return str(value) + class SegmentSequenceField(DataElementField): type = 'sf' diff --git a/fints/formals.py b/fints/formals.py index e207367..46b9bc0 100644 --- a/fints/formals.py +++ b/fints/formals.py @@ -6,71 +6,86 @@ from fints.utils import RepresentableEnum, ShortReprMixin CUSTOMER_ID_ANONYMOUS = '9999999999' + class DataElementGroup(Container): pass + class SegmentHeader(ShortReprMixin, DataElementGroup): - "Segmentkopf" + """Segmentkopf""" type = AlphanumericField(max_length=6, _d='Segmentkennung') number = NumericField(max_length=3, _d='Segmentnummer') version = NumericField(max_length=3, _d='Segmentversion') reference = NumericField(max_length=3, required=False, _d='Bezugssegment') + class ReferenceMessage(DataElementGroup): dialogue_id = DataElementField(type='id') message_number = NumericField(max_length=4) + class SecurityMethod(RepresentableEnum): DDV = 'DDV' RAH = 'RAH' RDH = 'RDH' PIN = 'PIN' + class SecurityProfile(DataElementGroup): - "Sicherheitsprofil" + """Sicherheitsprofil""" security_method = CodeField(enum=SecurityMethod, length=3, _d="Sicherheitsverfahren") security_method_version = DataElementField(type='num', _d="Version des Sicherheitsverfahrens") + class IdentifiedRole(RepresentableEnum): MS = '1' #: Message Sender MR = '2' #: Message Receiver + class SecurityIdentificationDetails(DataElementGroup): identified_role = CodeField(IdentifiedRole, max_length=3) cid = DataElementField(type='bin', max_length=256) identifier = DataElementField(type='id') + class DateTimeType(RepresentableEnum): - STS = '1' #: Sicherheitszeitstempel - CRT = '6' #: Certificate Revocation Time + STS = '1' #: Sicherheitszeitstempel + CRT = '6' #: Certificate Revocation Time + class SecurityDateTime(DataElementGroup): date_time_type = CodeField(DateTimeType, max_length=3) date = DataElementField(type='dat', required=False) time = DataElementField(type='tim', required=False) + class UsageEncryption(RepresentableEnum): - OSY = '2' #: Owner Symmetric + OSY = '2' #: Owner Symmetric + class OperationMode(RepresentableEnum): - CBC = '2' #: Cipher Block Chaining - ISO_9796_1 = '16' #: ISO 9796-1 (bei RDH) - ISO_9796_2_RANDOM = '17' #: ISO 9796-2 mit Zufallszahl (bei RDH) - PKCS1V15 = '18' #: RSASSA-PKCS#1 V1.5 (bei RDH); RSAES-PKCS#1 V1.5 (bei RAH, RDH) - PSS = '19' #: RSASSA-PSS (bei RAH, RDH) - ZZZ = '999' #: Gegenseitig vereinbart (DDV: Retail-MAC) + CBC = '2' #: Cipher Block Chaining + ISO_9796_1 = '16' #: ISO 9796-1 (bei RDH) + ISO_9796_2_RANDOM = '17' #: ISO 9796-2 mit Zufallszahl (bei RDH) + PKCS1V15 = '18' #: RSASSA-PKCS#1 V1.5 (bei RDH); RSAES-PKCS#1 V1.5 (bei RAH, RDH) + PSS = '19' #: RSASSA-PSS (bei RAH, RDH) + ZZZ = '999' #: Gegenseitig vereinbart (DDV: Retail-MAC) + class EncryptionAlgorithmCoded(RepresentableEnum): TWOKEY3DES = '13' #: 2-Key-Triple-DES AES256 = '14' #: AES-256 + class AlgorithmParameterName(RepresentableEnum): KYE = '5' #: Symmetrischer Schlüssel, verschlüsselt mit symmetrischem Schlüssel KYP = '6' #: Symmetrischer Schlüssel, verschlüsselt mit öffentlichem Schlüssel + class AlgorithmParameterIVName(RepresentableEnum): IVC = '1' #: Initialization value, clear text + class EncryptionAlgorithm(DataElementGroup): usage_encryption = CodeField(UsageEncryption, max_length=3) operation_mode = CodeField(OperationMode, max_length=3) @@ -80,26 +95,31 @@ class EncryptionAlgorithm(DataElementGroup): algorithm_parameter_iv_name = CodeField(AlgorithmParameterIVName, max_length=3) algorithm_parameter_iv_value = DataElementField(type='bin', max_length=512, required=False) + class HashAlgorithm(DataElementGroup): usage_hash = DataElementField(type='code', max_length=3) hash_algorithm = DataElementField(type='code', max_length=3) algorithm_parameter_name = DataElementField(type='code', max_length=3) algorithm_parameter_value = DataElementField(type='bin', max_length=512, required=False) + class SignatureAlgorithm(DataElementGroup): usage_signature = DataElementField(type='code', max_length=3) signature_algorithm = DataElementField(type='code', max_length=3) operation_mode = DataElementField(type='code', max_length=3) + class BankIdentifier(DataElementGroup): country_identifier = DataElementField(type='ctr') bank_code = DataElementField(type='an', max_length=30) + class KeyType(RepresentableEnum): - "Schlüsselart" - D = 'D' #: Schlüssel zur Erzeugung digitaler Signaturen - S = 'S' #: Signierschlüssel - V = 'V' #: Chiffrierschlüssel + """Schlüsselart""" + D = 'D' #: Schlüssel zur Erzeugung digitaler Signaturen + S = 'S' #: Signierschlüssel + V = 'V' #: Chiffrierschlüssel + class KeyName(DataElementGroup): bank_identifier = DataElementGroupField(type=BankIdentifier) @@ -108,20 +128,24 @@ class KeyName(DataElementGroup): key_number = DataElementField(type='num', max_length=3) key_version = DataElementField(type='num', max_length=3) + class Certificate(DataElementGroup): certificate_type = DataElementField(type='code') certificate_content = DataElementField(type='bin', max_length=4096) + class UserDefinedSignature(DataElementGroup): pin = PasswordField(max_length=99) tan = DataElementField(type='an', max_length=99, required=False) + class Response(DataElementGroup): code = DataElementField(type='dig', length=4) reference_element = DataElementField(type='an', max_length=7) text = DataElementField(type='an', max_length=80) parameters = DataElementField(type='an', max_length=35, max_count=10, required=False) + class Amount1(DataElementGroup): """Betrag @@ -129,16 +153,19 @@ class Amount1(DataElementGroup): amount = DataElementField(type='wrt', _d="Wert") currency = DataElementField(type='cur', _d="Währung") + class AccountInformation(DataElementGroup): account_number = DataElementField(type='id') subaccount_number = DataElementField(type='id') bank_identifier = DataElementGroupField(type=BankIdentifier) + class AccountLimit(DataElementGroup): limit_type = DataElementField(type='code', length=1) limit_amount = DataElementGroupField(type=Amount1, required=False) limit_days = DataElementField(type='num', max_length=3, required=False) + class AllowedTransaction(DataElementGroup): transaction = DataElementField(type='an', max_length=6) required_signatures = DataElementField(type='num', max_length=2) @@ -146,54 +173,64 @@ class AllowedTransaction(DataElementGroup): limit_amount = DataElementGroupField(type=Amount1, required=False) limit_days = DataElementField(type='num', max_length=3, required=False) + class TANTimeDialogAssociation(RepresentableEnum): - NOT_ALLOWED = '1' #: TAN nicht zeitversetzt / dialogübergreifend erlaubt - ALLOWED = '2' #: TAN zeitversetzt / dialogübergreifend erlaubt - BOTH = '3' #: beide Verfahren unterstützt - NOT_APPLICABLE = '4' #: nicht zutreffend + NOT_ALLOWED = '1' #: TAN nicht zeitversetzt / dialogübergreifend erlaubt + ALLOWED = '2' #: TAN zeitversetzt / dialogübergreifend erlaubt + BOTH = '3' #: beide Verfahren unterstützt + NOT_APPLICABLE = '4' #: nicht zutreffend + class AllowedFormat(RepresentableEnum): - NUMERIC = '1' #: numerisch - ALPHANUMERIC = '2' #: alfanumerisch + NUMERIC = '1' #: numerisch + ALPHANUMERIC = '2' #: alfanumerisch + class TANListNumberRequired(RepresentableEnum): - NO = '0' #: Nein - YES = '2' #: Ja + NO = '0' #: Nein + YES = '2' #: Ja + class InitialisationMode(RepresentableEnum): - CLEARTEXT_PIN_NO_TAN = '00' #: Initialisierungsverfahren mit Klartext-PIN und ohne TAN - ENCRYPTED_PIN_NO_TAN = '01' #: Schablone 01: Verschlüsselte PIN und ohne TAN - MASK_02 = '02' #: Schablone 02: Reserviert, bei FinTS zur Zeit nicht verwendet + CLEARTEXT_PIN_NO_TAN = '00' #: Initialisierungsverfahren mit Klartext-PIN und ohne TAN + ENCRYPTED_PIN_NO_TAN = '01' #: Schablone 01: Verschlüsselte PIN und ohne TAN + MASK_02 = '02' #: Schablone 02: Reserviert, bei FinTS zur Zeit nicht verwendet + class DescriptionRequired(RepresentableEnum): - MUST_NOT = '0' #: Bezeichnung des TAN-Mediums darf nicht angegeben werden - MAY = '1' #: Bezeichnung des TAN-Mediums kann angegeben werden - MUST = '2' #: Bezeichnung des TAN-Mediums muss angegeben werden + MUST_NOT = '0' #: Bezeichnung des TAN-Mediums darf nicht angegeben werden + MAY = '1' #: Bezeichnung des TAN-Mediums kann angegeben werden + MUST = '2' #: Bezeichnung des TAN-Mediums muss angegeben werden + class SMSChargeAccountRequired(RepresentableEnum): - MUST_NOT = '0' #: SMS-Abbuchungskonto darf nicht angegeben werden - MAY = '1' #: SMS-Abbuchungskonto kann angegeben werden - MUST = '2' #: SMS-Abbuchungskonto muss angegeben werden + MUST_NOT = '0' #: SMS-Abbuchungskonto darf nicht angegeben werden + MAY = '1' #: SMS-Abbuchungskonto kann angegeben werden + MUST = '2' #: SMS-Abbuchungskonto muss angegeben werden + class PrincipalAccountRequired(RepresentableEnum): - MUST_NOT = '0' #: Auftraggeberkonto darf nicht angegeben werden - MUST = '2' #: Auftraggeberkonto muss angegeben werden, wenn im Geschäftsvorfall enthalten + MUST_NOT = '0' #: Auftraggeberkonto darf nicht angegeben werden + MUST = '2' #: Auftraggeberkonto muss angegeben werden, wenn im Geschäftsvorfall enthalten + class TaskHashAlgorithm(RepresentableEnum): - NONE = '0' #: Auftrags-Hashwert nicht unterstützt - RIPEMD_160 = '1' #: RIPEMD-160 - SHA_1 = '2' #: SHA-1 + NONE = '0' #: Auftrags-Hashwert nicht unterstützt + RIPEMD_160 = '1' #: RIPEMD-160 + SHA_1 = '2' #: SHA-1 + class TwoStepParametersCommon(DataElementGroup): @property def VERSION(self): - "TAN mechanism version" + """TAN mechanism version""" return int( re.match(r'^(\D+)(\d+)$', self.__class__.__name__).group(2) ) security_function = DataElementField(type='code', max_length=3, _d="Sicherheitsfunktion kodiert") tan_process = DataElementField(type='code', length=1, _d="TAN-Prozess") tech_id = DataElementField(type='id', _d="Technische Identifikation TAN-Verfahren") + class TwoStepParameters1(TwoStepParametersCommon): name = DataElementField(type='an', max_length=30, _d="Name des Zwei-Schritt-Verfahrens") max_length_input = DataElementField(type='num', max_length=2, _d="Maximale Länge des Eingabewertes im Zwei-Schritt-Verfahren") @@ -204,6 +241,7 @@ class TwoStepParameters1(TwoStepParametersCommon): multiple_tans_allowed = DataElementField(type='jn', _d="Mehrfach-TAN erlaubt") tan_time_delayed_allowed = DataElementField(type='jn', _d="TAN zeitversetzt/dialogübergreifend erlaubt") + class TwoStepParameters2(TwoStepParametersCommon): name = DataElementField(type='an', max_length=30, _d="Name des Zwei-Schritt-Verfahrens") max_length_input = DataElementField(type='num', max_length=2, _d="Maximale Länge des Eingabewertes im Zwei-Schritt-Verfahren") @@ -218,6 +256,7 @@ class TwoStepParameters2(TwoStepParametersCommon): challenge_class_required = DataElementField(type='jn', _d="Challenge-Klasse erforderlich") challenge_value_required = DataElementField(type='jn', _d="Challenge-Betrag erforderlich") + class TwoStepParameters3(TwoStepParametersCommon): name = DataElementField(type='an', max_length=30, _d="Name des Zwei-Schritt-Verfahrens") max_length_input = DataElementField(type='num', max_length=2, _d="Maximale Länge des Eingabewertes im Zwei-Schritt-Verfahren") @@ -235,6 +274,7 @@ class TwoStepParameters3(TwoStepParametersCommon): description_required = CodeField(enum=DescriptionRequired, length=1, _d="Bezeichnung des TAN-Medium erforderlich") supported_media_number = DataElementField(type='num', length=1, required=False, _d="Anzahl unterstützter aktiver TAN-Medien") + class TwoStepParameters4(TwoStepParametersCommon): zka_id = DataElementField(type='an', max_length=32, _d="ZKA TAN-Verfahren") zka_version = DataElementField(type='an', max_length=10, _d="Version ZKA TAN-Verfahren") @@ -256,6 +296,7 @@ class TwoStepParameters4(TwoStepParametersCommon): description_required = CodeField(enum=DescriptionRequired, length=1, _d="Bezeichnung des TAN-Medium erforderlich") supported_media_number = DataElementField(type='num', length=1, required=False, _d="Anzahl unterstützter aktiver TAN-Medien") + class TwoStepParameters5(TwoStepParametersCommon): zka_id = DataElementField(type='an', max_length=32, _d="ZKA TAN-Verfahren") zka_version = DataElementField(type='an', max_length=10, _d="Version ZKA TAN-Verfahren") @@ -277,6 +318,7 @@ class TwoStepParameters5(TwoStepParametersCommon): description_required = CodeField(enum=DescriptionRequired, length=1, _d="Bezeichnung des TAN-Medium erforderlich") supported_media_number = DataElementField(type='num', length=1, required=False, _d="Anzahl unterstützter aktiver TAN-Medien") + class TwoStepParameters6(TwoStepParametersCommon): zka_id = DataElementField(type='an', max_length=32, _d="ZKA TAN-Verfahren") zka_version = DataElementField(type='an', max_length=10, _d="Version ZKA TAN-Verfahren") @@ -297,34 +339,43 @@ class TwoStepParameters6(TwoStepParametersCommon): response_hhd_uc_required = DataElementField(type='jn', _d="Antwort HHD_UC erforderlich") supported_media_number = DataElementField(type='num', length=1, required=False, _d="Anzahl unterstützter aktiver TAN-Medien") + class ParameterTwostepCommon(DataElementGroup): onestep_method_allowed = DataElementField(type='jn') multiple_tasks_allowed = DataElementField(type='jn') task_hash_algorithm = CodeField(enum=TaskHashAlgorithm, length=1, _d="Auftrags-Hashwertverfahren") + class ParameterTwostepTAN1(ParameterTwostepCommon): security_profile_bank_signature = DataElementField(type='code', length=1) twostep_parameters = DataElementGroupField(type=TwoStepParameters1, min_count=1, max_count=98) + class ParameterTwostepTAN2(ParameterTwostepCommon): twostep_parameters = DataElementGroupField(type=TwoStepParameters2, min_count=1, max_count=98) + class ParameterTwostepTAN3(ParameterTwostepCommon): twostep_parameters = DataElementGroupField(type=TwoStepParameters3, min_count=1, max_count=98) + class ParameterTwostepTAN4(ParameterTwostepCommon): twostep_parameters = DataElementGroupField(type=TwoStepParameters4, min_count=1, max_count=98) + class ParameterTwostepTAN5(ParameterTwostepCommon): twostep_parameters = DataElementGroupField(type=TwoStepParameters5, min_count=1, max_count=98) + class ParameterTwostepTAN6(ParameterTwostepCommon): twostep_parameters = DataElementGroupField(type=TwoStepParameters6, min_count=1, max_count=98) + class TransactionTanRequired(DataElementGroup): transaction = DataElementField(type='an', max_length=6) tan_required = DataElementField(type='jn') + class ParameterPinTan(DataElementGroup): min_pin_length = DataElementField(type='num', max_length=2, required=False) max_pin_length = DataElementField(type='num', max_length=2, required=False) @@ -333,21 +384,25 @@ class ParameterPinTan(DataElementGroup): customer_id_field_text = DataElementField(type='an', max_length=30, required=False) transaction_tans_required = DataElementGroupField(type=TransactionTanRequired, max_count=999, required=False) + class Language2(RepresentableEnum): """Dialogsprache Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals""" - STANDARD = '0' #: Standard - DE = '1' #: Deutsch, 'de', Subset Deutsch, Codeset 1 (Latin 1) - EN = '2' #: Englisch, 'en', Subset Englisch, Codeset 1 (Latin 1) - FR = '3' #: Französisch, 'fr', Subset Französisch, Codeset 1 (Latin 1) + STANDARD = '0' #: Standard + DE = '1' #: Deutsch, 'de', Subset Deutsch, Codeset 1 (Latin 1) + EN = '2' #: Englisch, 'en', Subset Englisch, Codeset 1 (Latin 1) + FR = '3' #: Französisch, 'fr', Subset Französisch, Codeset 1 (Latin 1) + class SupportedLanguages2(DataElementGroup): languages = CodeField(enum=Language2, max_length=3, min_count=1, max_count=9) + class SupportedHBCIVersions2(DataElementGroup): versions = DataElementField(type='code', max_length=3, min_count=1, max_count=9) + class KTZ1(DataElementGroup): """Kontoverbindung ZV international, version 1 @@ -382,6 +437,7 @@ class KTZ1(DataElementGroup): ) ) + class KTI1(DataElementGroup): """Kontoverbindung international, version 1 @@ -405,6 +461,7 @@ class KTI1(DataElementGroup): ) ) + class Account2(DataElementGroup): """Kontoverbindung, version 2 @@ -423,6 +480,7 @@ class Account2(DataElementGroup): bank_code=acc.blz, ) + class Account3(DataElementGroup): """Kontoverbindung, version 3 @@ -442,6 +500,7 @@ class Account3(DataElementGroup): ) ) + class SecurityRole(RepresentableEnum): """Rolle des Sicherheitslieferanten, kodiert, version 2 @@ -449,23 +508,25 @@ class SecurityRole(RepresentableEnum): Die Wahl ist von der bankfachlichen Auslegung der Signatur, respektive vom vertraglichen Zustand zwischen Kunde und Kreditinstitut abhängig. Source: FinTS Financial Transaction Services, Sicherheitsverfahren HBCI""" - ISS = '1' #: Erfasser, Erstsignatur - CON = '3' #: Unterstützer, Zweitsignatur - WIT = '4' #: Zeuge/Übermittler, nicht Erfasser + ISS = '1' #: Erfasser, Erstsignatur + CON = '3' #: Unterstützer, Zweitsignatur + WIT = '4' #: Zeuge/Übermittler, nicht Erfasser + class CompressionFunction(RepresentableEnum): """Komprimierungsfunktion, version 2 Source: FinTS Financial Transaction Services, Sicherheitsverfahren HBCI""" - NULL = '0' #: Keine Kompression - LZW = '1' #: Lempel, Ziv, Welch - COM = '2' #: Optimized LZW - LZSS = '3' #: Lempel, Ziv - LZHuf = '4' #: LZ + Huffman Coding - ZIP = '5' #: PKZIP - GZIP = '6' #: deflate (http://www.gzip.org/zlib) - BZIP2 = '7' #: bzip2 (http://sourceware.cygnus.com/bzip2/) - ZZZ = '999' #: Gegenseitig vereinbart + NULL = '0' #: Keine Kompression + LZW = '1' #: Lempel, Ziv, Welch + COM = '2' #: Optimized LZW + LZSS = '3' #: Lempel, Ziv + LZHuf = '4' #: LZ + Huffman Coding + ZIP = '5' #: PKZIP + GZIP = '6' #: deflate (http://www.gzip.org/zlib) + BZIP2 = '7' #: bzip2 (http://sourceware.cygnus.com/bzip2/) + ZZZ = '999' #: Gegenseitig vereinbart + class SecurityApplicationArea(RepresentableEnum): """Bereich der Sicherheitsapplikation, kodiert, version 2 @@ -473,8 +534,9 @@ class SecurityApplicationArea(RepresentableEnum): Informationen darüber, welche Daten vom kryptographischen Prozess verarbeitet werden. Source: FinTS Financial Transaction Services, Sicherheitsverfahren HBCI""" - SHM = '1' #: Signaturkopf und HBCI-Nutzdaten - SHT = '2' #: Von Signaturkopf bis Signaturabschluss + SHM = '1' #: Signaturkopf und HBCI-Nutzdaten + SHT = '2' #: Von Signaturkopf bis Signaturabschluss + class SecurityClass(RepresentableEnum): """Sicherheitsklasse, version 1 @@ -482,11 +544,12 @@ class SecurityClass(RepresentableEnum): Die Sicherheitsklasse gibt für jede Signatur den erforderlichen Sicherheitsdienst an. Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals""" - NONE = 0 #: Kein Sicherheitsdienst erforderlich - AUTH = 1 #: Sicherheitsdienst 'Authentikation' - AUTH_ADV = 2 #: Sicherheitsdienst 'Authentikation' mit fortgeschrittener elektronischer Signatur, optionaler Zertifikatsprüfung - NON_REPUD = 3 #: Sicherheitsdienst 'Non-Repudiation' mit fortgeschrittener elektronischer Signatur, optionaler Zertifikatsprüfung - NON_REPUD_QUAL = 4 #: Sicherheitsdienst 'Non-Repudiation' mit fortgeschrittener bzw. qualifizierter elektronischer Signatur, zwingende Zertifikatsprüfung + NONE = 0 #: Kein Sicherheitsdienst erforderlich + AUTH = 1 #: Sicherheitsdienst 'Authentikation' + AUTH_ADV = 2 #: Sicherheitsdienst 'Authentikation' mit fortgeschrittener elektronischer Signatur, optionaler Zertifikatsprüfung + NON_REPUD = 3 #: Sicherheitsdienst 'Non-Repudiation' mit fortgeschrittener elektronischer Signatur, optionaler Zertifikatsprüfung + NON_REPUD_QUAL = 4 #: Sicherheitsdienst 'Non-Repudiation' mit fortgeschrittener bzw. qualifizierter elektronischer Signatur, zwingende Zertifikatsprüfung + class UPDUsage(RepresentableEnum): """UPD-Verwendung, version 2 @@ -494,30 +557,34 @@ class UPDUsage(RepresentableEnum): Kennzeichen dafür, wie diejenigen Geschäftsvorfälle zu interpretieren sind, die bei der Beschreibung der Kontoinformationen nicht unter den erlaubten Geschäftsvorfällen aufgeführt sind. Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals""" - UPD_CONCLUSIVE = '0' #: Die nicht aufgeführten Geschäftsvorfälle sind gesperrt - UPD_INCONCLUSIVE = '1' #: Bei nicht aufgeführten Geschäftsvorfällen ist keine Aussage möglich, ob diese erlaubt oder gesperrt sind + UPD_CONCLUSIVE = '0' #: Die nicht aufgeführten Geschäftsvorfälle sind gesperrt + UPD_INCONCLUSIVE = '1' #: Bei nicht aufgeführten Geschäftsvorfällen ist keine Aussage möglich, ob diese erlaubt oder gesperrt sind + class SystemIDStatus(RepresentableEnum): """Kundensystem-Status, version 2 Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals""" - ID_UNNECESSARY = '0' #: Kundensystem-ID wird nicht benötigt - ID_NECESSARY = '1' #: Kundensystem-ID wird benötigt + ID_UNNECESSARY = '0' #: Kundensystem-ID wird nicht benötigt + ID_NECESSARY = '1' #: Kundensystem-ID wird benötigt + class SynchronisationMode(RepresentableEnum): """Synchronisierungsmodus, version 2 Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals""" - NEW_SYSTEM_ID = '0' #: Neue Kundensystem-ID zurückmelden - LAST_MESSAGE = '1' #: Letzte verarbeitete Nachrichtennummer zurückmelden - SIGNATURE_ID = '2' #: Signatur-ID zurückmelden + NEW_SYSTEM_ID = '0' #: Neue Kundensystem-ID zurückmelden + LAST_MESSAGE = '1' #: Letzte verarbeitete Nachrichtennummer zurückmelden + SIGNATURE_ID = '2' #: Signatur-ID zurückmelden + class CreditDebit2(RepresentableEnum): """Soll-Haben-Kennzeichen, version 2 Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """ - CREDIT = 'C' #: Haben - DEBIT = 'D' #: Soll + CREDIT = 'C' #: Haben + DEBIT = 'D' #: Soll + class Balance1(DataElementGroup): """Saldo, version 1 @@ -538,6 +605,7 @@ class Balance1(DataElementGroup): currency=self.currency ) + class Balance2(DataElementGroup): """Saldo, version 2 @@ -556,6 +624,7 @@ class Balance2(DataElementGroup): currency=self.amount.currency ) + class Timestamp1(DataElementGroup): """Zeitstempel @@ -563,14 +632,15 @@ class Timestamp1(DataElementGroup): date = DataElementField(type='dat', _d="Datum") time = DataElementField(type='tim', required=False, _d="Uhrzeit") + class TANMediaType2(RepresentableEnum): """TAN-Medium-Art Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Sicherheitsverfahren PIN/TAN""" - ALL = '0' #: Alle - ACTIVE = '1' #: Aktiv - AVAILABLE = '2' #: Verfügbar + ALL = '0' #: Alle + ACTIVE = '1' #: Aktiv + AVAILABLE = '2' #: Verfügbar class TANMediaClass3(RepresentableEnum): @@ -578,33 +648,35 @@ class TANMediaClass3(RepresentableEnum): Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Sicherheitsverfahren PIN/TAN""" - ALL = 'A' #: Alle Medien - LIST = 'L' #: Liste - GENERATOR = 'G' #: TAN-Generator - MOBILE = 'M' #: Mobiltelefon mit mobileTAN - SECODER = 'S' #: Secoder + ALL = 'A' #: Alle Medien + LIST = 'L' #: Liste + GENERATOR = 'G' #: TAN-Generator + MOBILE = 'M' #: Mobiltelefon mit mobileTAN + SECODER = 'S' #: Secoder class TANMediaClass4(RepresentableEnum): """TAN-Medium-Klasse, version 4 Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Sicherheitsverfahren PIN/TAN""" - ALL = 'A' #: Alle Medien - LIST = 'L' #: Liste - GENERATOR = 'G' #: TAN-Generator - MOBILE = 'M' #: Mobiltelefon mit mobileTAN - SECODER = 'S' #: Secoder - BILATERAL = 'B' #: Bilateral vereinbart + ALL = 'A' #: Alle Medien + LIST = 'L' #: Liste + GENERATOR = 'G' #: TAN-Generator + MOBILE = 'M' #: Mobiltelefon mit mobileTAN + SECODER = 'S' #: Secoder + BILATERAL = 'B' #: Bilateral vereinbart + class TANMediumStatus(RepresentableEnum): """Status Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Sicherheitsverfahren PIN/TAN""" - ACTIVE = '1' #: Aktiv - AVAILABLE = '2' #: Verfügbar - ACTIVE_SUCCESSOR = '3' #: Aktiv Folgekarte - AVAILABLE_SUCCESSOR = '4' #: Verfügbar Folgekarte + ACTIVE = '1' #: Aktiv + AVAILABLE = '2' #: Verfügbar + ACTIVE_SUCCESSOR = '3' #: Aktiv Folgekarte + AVAILABLE_SUCCESSOR = '4' #: Verfügbar Folgekarte + class TANMedia4(DataElementGroup): """TAN-Medium-Liste, version 4 @@ -628,6 +700,7 @@ class TANMedia4(DataElementGroup): last_use = DataElementField(type='dat', required=False, _d="Letzte Benutzung") active_since = DataElementField(type='dat', required=False, _d="Freigeschaltet am") + class TANMedia5(DataElementGroup): """TAN-Medium-Liste, version 5 @@ -651,14 +724,16 @@ class TANMedia5(DataElementGroup): last_use = DataElementField(type='dat', required=False, _d="Letzte Benutzung") active_since = DataElementField(type='dat', required=False, _d="Freigeschaltet am") + class TANUsageOption(RepresentableEnum): """TAN-Einsatzoption Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Sicherheitsverfahren PIN/TAN""" - ALL_ACTIVE = '0' #: Kunde kann alle "aktiven" Medien parallel nutzen - EXACTLY_ONE = '1'#: Kunde kann genau ein Medium zu einer Zeit nutzen - MOBILE_PLUS_GENERATOR = '2' #: Kunde kann ein Mobiltelefon und einen TAN-Generator parallel nutzen + ALL_ACTIVE = '0' #: Kunde kann alle "aktiven" Medien parallel nutzen + EXACTLY_ONE = '1' #: Kunde kann genau ein Medium zu einer Zeit nutzen + MOBILE_PLUS_GENERATOR = '2' #: Kunde kann ein Mobiltelefon und einen TAN-Generator parallel nutzen + class ParameterChallengeClass(DataElementGroup): """Parameter Challenge-Klasse @@ -666,6 +741,7 @@ class ParameterChallengeClass(DataElementGroup): Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Sicherheitsverfahren PIN/TAN""" parameters = DataElementField(type='an', max_length=999, count=9, required=False) + class ResponseHHDUC(DataElementGroup): """Antwort HHD_UC @@ -676,6 +752,7 @@ class ResponseHHDUC(DataElementGroup): cvr = DataElementField(type='bin', max_length=256, _d="CVR") version_info_chiptan = DataElementField(type='bin', max_length=256, _d="Versionsinfo der chipTAN-Applikation") + class ChallengeValidUntil(DataElementGroup): """Gültigkeitsdatum und -uhrzeit für Challenge @@ -683,6 +760,7 @@ class ChallengeValidUntil(DataElementGroup): date = DataElementField(type='dat', _d="Datum") time = DataElementField(type='tim', _d="Uhrzeit") + class BatchTransferParameter1(DataElementGroup): """Parameter SEPA-Sammelüberweisung, version 1 @@ -691,10 +769,12 @@ class BatchTransferParameter1(DataElementGroup): sum_amount_required = DataElementField(type='jn', _d="Summenfeld benötigt") single_booking_allowed = DataElementField(type='jn', _d="Einzelbuchung erlaubt") + class ServiceType2(RepresentableEnum): - T_ONLINE = 1 #: T-Online - TCP_IP = 2 #: TCP/IP (Protokollstack SLIP/PPP) - HTTPS = 3 #: https + T_ONLINE = 1 #: T-Online + TCP_IP = 2 #: TCP/IP (Protokollstack SLIP/PPP) + HTTPS = 3 #: https + class CommunicationParameter2(DataElementGroup): """Kommunikationsparameter, version 2 @@ -706,6 +786,7 @@ class CommunicationParameter2(DataElementGroup): filter_function = DataElementField(type='an', length=3, required=False, _d="Filterfunktion") filter_function_version = DataElementField(type='num', max_length=3, required=False, _d="Version der Filterfunktion") + class ScheduledDebitParameter1(DataElementGroup): """Parameter terminierte SEPA-Einzellastschrift einreichen, version 1 @@ -715,6 +796,7 @@ class ScheduledDebitParameter1(DataElementGroup): min_advance_notice_FRST_OOFF = DataElementField(type='num', max_length=4, _d="Minimale Vorlaufzeit FRST/OOFF") max_advance_notice_FRST_OOFF = DataElementField(type='num', max_length=4, _d="Maximale Vorlaufzeit FRST/OOFF") + class ScheduledDebitParameter2(DataElementGroup): """Parameter terminierte SEPA-Einzellastschrift einreichen, version 2 @@ -724,6 +806,7 @@ class ScheduledDebitParameter2(DataElementGroup): allowed_purpose_codes = DataElementField(type='an', max_length=4096, required=False, _d="Zulässige purpose codes") supported_sepa_formats = DataElementField(type='an', max_length=256, max_count=9, required=False, _d="Unterstützte SEPA-Datenformate") + class ScheduledBatchDebitParameter1(DataElementGroup): """Parameter terminierte SEPA-Sammellastschrift einreichen, version 1 @@ -736,6 +819,7 @@ class ScheduledBatchDebitParameter1(DataElementGroup): sum_amount_required = DataElementField(type='jn', _d="Summenfeld benötigt") single_booking_allowed = DataElementField(type='jn', _d="Einzelbuchung erlaubt") + class ScheduledBatchDebitParameter2(DataElementGroup): """Parameter terminierte SEPA-Sammellastschrift einreichen, version 2 @@ -748,6 +832,7 @@ class ScheduledBatchDebitParameter2(DataElementGroup): allowed_purpose_codes = DataElementField(type='an', max_length=4096, required=False, _d="Zulässige purpose codes") supported_sepa_formats = DataElementField(type='an', max_length=256, max_count=9, required=False, _d="Unterstützte SEPA-Datenformate") + class ScheduledCOR1DebitParameter1(DataElementGroup): """Parameter terminierte SEPA-COR1-Einzellastschrift, version 1 @@ -759,6 +844,7 @@ class ScheduledCOR1DebitParameter1(DataElementGroup): allowed_purpose_codes = DataElementField(type='an', max_length=4096, required=False, _d="Zulässige purpose codes") supported_sepa_formats = DataElementField(type='an', max_length=256, max_count=9, required=False, _d="Unterstützte SEPA-Datenformate") + class ScheduledCOR1BatchDebitParameter1(DataElementGroup): """Parameter terminierte SEPA-COR1-Sammellastschrift, version 1 @@ -780,6 +866,7 @@ class SupportedSEPAPainMessages1(DataElementGroup): Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """ sepa_descriptors = DataElementField(type='an', max_length=256, max_count=99, _d="SEPA Descriptor") + class QueryScheduledDebitParameter1(DataElementGroup): """Parameter Bestand terminierter SEPA-Einzellastschriften, version 1 @@ -787,6 +874,7 @@ class QueryScheduledDebitParameter1(DataElementGroup): date_range_allowed = DataElementField(type='jn', _d="Zeitraum möglich") max_number_responses_allowed = DataElementField(type='jn', _d="Eingabe Anzahl Einträge erlaubt") + class QueryScheduledDebitParameter2(DataElementGroup): """Parameter Bestand terminierter SEPA-Einzellastschriften, version 2 @@ -795,6 +883,7 @@ class QueryScheduledDebitParameter2(DataElementGroup): date_range_allowed = DataElementField(type='jn', _d="Zeitraum möglich") supported_sepa_formats = DataElementField(type='an', max_length=256, max_count=9, required=False, _d="Unterstützte SEPA-Datenformate") + class QueryScheduledBatchDebitParameter1(DataElementGroup): """Parameter Bestand terminierter SEPA-Sammellastschriften, version 1 @@ -802,6 +891,7 @@ class QueryScheduledBatchDebitParameter1(DataElementGroup): max_number_responses_allowed = DataElementField(type='jn', _d="Eingabe Anzahl Einträge erlaubt") date_range_allowed = DataElementField(type='jn', _d="Zeitraum möglich") + class QueryCreditCardStatements2(DataElementGroup): """Parameter Kreditkartenumsätze anfordern, version 2 @@ -810,18 +900,21 @@ class QueryCreditCardStatements2(DataElementGroup): max_number_responses_allowed = DataElementField(type='jn', _d="Eingabe Anzahl Einträge erlaubt") date_range_allowed = DataElementField(type='jn', _d="Zeitraum möglich") + class SEPACCode1(RepresentableEnum): - REVERSAL = '1' #: Reversal - REVOCATION = '2' #: Revocation - DELETE = '3' #: Delete + REVERSAL = '1' #: Reversal + REVOCATION = '2' #: Revocation + DELETE = '3' #: Delete + class StatusSEPATask1(RepresentableEnum): ## FIXME Enum names in english - PENDING = '1' #: In Terminierung - DECLINED = '2' #: Abgelehnt von erster Inkassostelle - IN_PROGRESS = '3' #: in Bearbeitung - PROCESSED = '4' #: Creditoren-seitig verarbeitet, Buchung veranlasst - REVOKED = '5' #: R-Transaktion wurde veranlasst + PENDING = '1' #: In Terminierung + DECLINED = '2' #: Abgelehnt von erster Inkassostelle + IN_PROGRESS = '3' #: in Bearbeitung + PROCESSED = '4' #: Creditoren-seitig verarbeitet, Buchung veranlasst + REVOKED = '5' #: R-Transaktion wurde veranlasst + class GetSEPAAccountParameter1(DataElementGroup): """Parameter SEPA-Kontoverbindung anfordern, version 1 diff --git a/fints/message.py b/fints/message.py index adced11..2ed621f 100644 --- a/fints/message.py +++ b/fints/message.py @@ -9,6 +9,7 @@ class MessageDirection(Enum): FROM_CUSTOMER = 1 FROM_INSTITUTE = 2 + class FinTSMessage(SegmentSequence): DIRECTION = None # Auto-Numbering, dialog relation, security base @@ -37,9 +38,11 @@ class FinTSMessage(SegmentSequence): if code is None or response.code == code: yield response + class FinTSCustomerMessage(FinTSMessage): DIRECTION = MessageDirection.FROM_CUSTOMER # Identification, authentication + class FinTSInstituteMessage(FinTSMessage): DIRECTION = MessageDirection.FROM_INSTITUTE diff --git a/fints/parser.py b/fints/parser.py index 2978b3f..998d2c4 100644 --- a/fints/parser.py +++ b/fints/parser.py @@ -44,12 +44,15 @@ from .segments.base import FinTS3Segment #: consumed, even in the presence of errors in this library. robust_mode = True + class FinTSParserWarning(UserWarning): pass + class FinTSParserError(ValueError): pass + TOKEN_RE = re.compile(rb""" ^(?: (?: \? (?P.) ) | (?P[^?:+@']+) @@ -57,6 +60,7 @@ TOKEN_RE = re.compile(rb""" | (?: @ (?P[0-9]+) @ ) )""", re.X | re.S) + class Token(Enum): EOF = 'eof' CHAR = 'char' @@ -65,6 +69,7 @@ class Token(Enum): COLON = ':' APOSTROPHE = "'" + class ParserState: def __init__(self, data: bytes, start=0, end=None, encoding='iso-8859-1'): self._token = None @@ -132,6 +137,7 @@ class ParserState: yield Token.EOF, b'' + class FinTS3Parser: """Parser for FinTS/HBCI 3.0 messages """ @@ -225,7 +231,6 @@ class FinTS3Parser: return retval - def parse_deg(self, clazz, data_i, required=True): retval = clazz() @@ -275,7 +280,6 @@ class FinTS3Parser: return retval - @staticmethod def explode_segments(data: bytes, start=0, end=None): segments = [] @@ -313,6 +317,7 @@ class FinTS3Parser: return segments + class FinTS3Serializer: """Serializer for FinTS/HBCI 3.0 messages """ @@ -422,7 +427,6 @@ class FinTS3Serializer: return result - @staticmethod def implode_segments(message: list): level1 = [] diff --git a/fints/security.py b/fints/security.py index 66f0164..f2789a4 100644 --- a/fints/security.py +++ b/fints/security.py @@ -1,6 +1,7 @@ import datetime import random +from fints.exceptions import FinTSError from .formals import ( AlgorithmParameterIVName, AlgorithmParameterName, CompressionFunction, DateTimeType, EncryptionAlgorithm, EncryptionAlgorithmCoded, @@ -21,6 +22,7 @@ class EncryptionMechanism: def decrypt(self, message: FinTSMessage): raise NotImplemented() + class AuthenticationMechanism: def sign_prepare(self, message: FinTSMessage): raise NotImplemented() @@ -31,6 +33,7 @@ class AuthenticationMechanism: def verify(self, message: FinTSMessage): raise NotImplemented() + class PinTanDummyEncryptionMechanism(EncryptionMechanism): def __init__(self, security_method_version=1): super().__init__() @@ -45,7 +48,8 @@ class PinTanDummyEncryptionMechanism(EncryptionMechanism): _now = datetime.datetime.now() - message.segments.insert(1, + message.segments.insert( + 1, HNVSK3( security_profile=SecurityProfile(SecurityMethod.PIN, self.security_method_version), security_function='998', @@ -78,7 +82,8 @@ class PinTanDummyEncryptionMechanism(EncryptionMechanism): ) ) message.segments[1].header.number = 998 - message.segments.insert(2, + message.segments.insert( + 2, HNVSD1( data=SegmentSequence(segments=plain_segments) ) @@ -142,10 +147,10 @@ class PinTanAuthenticationMechanism(AuthenticationMechanism): def sign_commit(self, message: FinTSMessage): if not self.pending_signature: - raise Error("No signature is pending") + raise FinTSError("No signature is pending") if self.pending_signature not in message.segments: - raise Error("Cannot sign a message that was not prepared") + raise FinTSError("Cannot sign a message that was not prepared") signature = HNSHA2( security_reference = self.pending_signature.security_reference, @@ -161,11 +166,13 @@ class PinTanAuthenticationMechanism(AuthenticationMechanism): def verify(self, message: FinTSMessage): pass + class PinTanOneStepAuthenticationMechanism(PinTanAuthenticationMechanism): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.security_function = '999' + class PinTanTwoStepAuthenticationMechanism(PinTanAuthenticationMechanism): def __init__(self, client, security_function, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/fints/segments/accounts.py b/fints/segments/accounts.py index 3a8cc8e..b677831 100644 --- a/fints/segments/accounts.py +++ b/fints/segments/accounts.py @@ -10,6 +10,7 @@ class HKSPA1(FinTS3Segment): """ accounts = DataElementGroupField(type=Account3, max_count=999, required=False, _d="Kontoverbindung") + class HISPA1(FinTS3Segment): """SEPA-Kontoverbindung rückmelden, version 1 @@ -17,6 +18,7 @@ class HISPA1(FinTS3Segment): """ accounts = DataElementGroupField(type=KTZ1, max_count=999, required=False, _d="SEPA-Kontoverbindung") + class HISPAS1(ParameterSegment): """SEPA-Kontoverbindung anfordern, Parameter, version 1 diff --git a/fints/segments/auth.py b/fints/segments/auth.py index 5ca7a8f..39d294e 100644 --- a/fints/segments/auth.py +++ b/fints/segments/auth.py @@ -96,6 +96,7 @@ class HITAN3(FinTS3Segment): ben = DataElementField(type='an', max_length=99, required=False, _d="BEN") tan_medium_name = DataElementField(type='an', max_length=32, required=False, _d="Bezeichnung des TAN-Mediums") + class HITAN5(FinTS3Segment): """Zwei-Schritt-TAN-Einreichung Rückmeldung, version 5 @@ -110,6 +111,7 @@ class HITAN5(FinTS3Segment): ben = DataElementField(type='an', max_length=99, required=False, _d="BEN") tan_medium_name = DataElementField(type='an', max_length=32, required=False, _d="Bezeichnung des TAN-Mediums") + class HITAN6(FinTS3Segment): """Zwei-Schritt-TAN-Einreichung Rückmeldung, version 6 @@ -122,6 +124,7 @@ class HITAN6(FinTS3Segment): challenge_valid_until = DataElementGroupField(type=ChallengeValidUntil, required=False, _d="Gültigkeitsdatum und -uhrzeit für Challenge") tan_medium_name = DataElementField(type='an', max_length=32, required=False, _d="Bezeichnung des TAN-Mediums") + class HKTAB4(FinTS3Segment): """TAN-Generator/Liste anzeigen Bestand, version 4 @@ -130,6 +133,7 @@ class HKTAB4(FinTS3Segment): tan_media_type = CodeField(enum=TANMediaType2, _d="TAN-Medium-Art") tan_media_class = CodeField(enum=TANMediaClass3, _d="TAN-Medium-Klasse") + class HITAB4(FinTS3Segment): """TAN-Generator/Liste anzeigen Bestand Rückmeldung, version 4 @@ -138,6 +142,7 @@ class HITAB4(FinTS3Segment): tan_usage_option = CodeField(enum=TANUsageOption, _d="TAN_Einsatzoption") tan_media_list = DataElementGroupField(type=TANMedia4, max_count=99, required=False, _d="TAN-Medium-Liste") + class HKTAB5(FinTS3Segment): """TAN-Generator/Liste anzeigen Bestand, version 5 @@ -146,6 +151,7 @@ class HKTAB5(FinTS3Segment): tan_media_type = CodeField(enum=TANMediaType2, _d="TAN-Medium-Art") tan_media_class = CodeField(enum=TANMediaClass4, _d="TAN-Medium-Klasse") + class HITAB5(FinTS3Segment): """TAN-Generator/Liste anzeigen Bestand Rückmeldung, version 5 @@ -154,27 +160,35 @@ class HITAB5(FinTS3Segment): tan_usage_option = CodeField(enum=TANUsageOption, _d="TAN_Einsatzoption") tan_media_list = DataElementGroupField(type=TANMedia5, max_count=99, required=False, _d="TAN-Medium-Liste") + class HITANSBase(ParameterSegment): pass + class HITANS1(HITANSBase): parameter = DataElementGroupField(type=ParameterTwostepTAN1) + class HITANS2(HITANSBase): parameter = DataElementGroupField(type=ParameterTwostepTAN2) + class HITANS3(HITANSBase): parameter = DataElementGroupField(type=ParameterTwostepTAN3) + class HITANS4(HITANSBase): parameter = DataElementGroupField(type=ParameterTwostepTAN4) + class HITANS5(HITANSBase): parameter = DataElementGroupField(type=ParameterTwostepTAN5) + class HITANS6(HITANSBase): parameter = DataElementGroupField(type=ParameterTwostepTAN6) + class HIPINS1(ParameterSegment): """PIN/TAN-spezifische Informationen, version 1 diff --git a/fints/segments/bank.py b/fints/segments/bank.py index 812ff3b..1b41a74 100644 --- a/fints/segments/bank.py +++ b/fints/segments/bank.py @@ -22,16 +22,18 @@ class HIBPA3(FinTS3Segment): min_timeout = DataElementField(type='num', max_length=4, required=False, _d="Minimaler Timeout-Wert") max_timeout = DataElementField(type='num', max_length=4, required=False, _d="Maximaler Timeout-Wert") + class HIUPA4(FinTS3Segment): - "Userparameter allgemein" + """Userparameter allgemein""" user_identifier = DataElementField(type='id', _d="Benutzerkennung") upd_version = DataElementField(type='num', max_length=3, _d="UPD-Version") upd_usage = CodeField(UPDUsage, length=1, _d="UPD-Verwendung") username = DataElementField(type='an', max_length=35, required=False, _d="Benutzername") extension = DataElementField(type='an', max_length=2048, required=False, _d="Erweiterung, allgemein") + class HIUPD6(FinTS3Segment): - "Kontoinformationen" + """Kontoinformationen""" account_information = DataElementGroupField(type=AccountInformation, required=False, _d="Kontoverbindung") iban = DataElementField(type='an', max_length=34, _d="IBAN") customer_id = DataElementField(type='id', _d="Kunden-ID") @@ -44,6 +46,7 @@ class HIUPD6(FinTS3Segment): allowed_transactions = DataElementGroupField(type=AllowedTransaction, count=999, required=False, _d="Erlaubte Geschäftsvorfälle") extension = DataElementField(type='an', max_length=2048, required=False, _d="Erweiterung, kontobezogen") + class HKKOM4(FinTS3Segment): """Kommunikationszugang anfordern, version 4 @@ -53,6 +56,7 @@ class HKKOM4(FinTS3Segment): max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge") touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt") + class HIKOM4(FinTS3Segment): """Kommunikationszugang rückmelden, version 4 diff --git a/fints/segments/base.py b/fints/segments/base.py index cc0bb74..518864e 100644 --- a/fints/segments/base.py +++ b/fints/segments/base.py @@ -7,6 +7,7 @@ from fints.utils import SubclassesMixin, classproperty TYPE_VERSION_RE = re.compile(r'^([A-Z]+)(\d+)$') + class FinTS3SegmentMeta(ContainerMeta): @staticmethod def _check_fields_recursive(instance): @@ -21,6 +22,7 @@ class FinTS3SegmentMeta(ContainerMeta): FinTS3SegmentMeta._check_fields_recursive(retval) return retval + class FinTS3Segment(Container, SubclassesMixin, metaclass=FinTS3SegmentMeta): header = DataElementGroupField(type=SegmentHeader, _d="Segmentkopf") @@ -63,6 +65,7 @@ class ParameterSegment_22(FinTS3Segment): max_number_tasks = DataElementField(type='num', max_length=3, _d="Maximale Anzahl Aufträge") min_number_signatures = DataElementField(type='num', length=1, _d="Anzahl Signaturen mindestens") + class ParameterSegment(FinTS3Segment): max_number_tasks = DataElementField(type='num', max_length=3, _d="Maximale Anzahl Aufträge") min_number_signatures = DataElementField(type='num', length=1, _d="Anzahl Signaturen mindestens") diff --git a/fints/segments/debit.py b/fints/segments/debit.py index 9d64d8b..a0220f4 100644 --- a/fints/segments/debit.py +++ b/fints/segments/debit.py @@ -17,6 +17,7 @@ class BatchDebitBase(FinTS3Segment): sepa_descriptor = DataElementField(type='an', max_length=256, _d="SEPA Descriptor") sepa_pain_message = DataElementField(type='bin', _d="SEPA pain message") + class DebitResponseBase(FinTS3Segment): task_id = DataElementField(type='an', max_length=99, required=False, _d="Auftragsidentifikation") @@ -52,6 +53,7 @@ class HKDSE2(FinTS3Segment): sepa_descriptor = DataElementField(type='an', max_length=256, _d="SEPA Descriptor") sepa_pain_message = DataElementField(type='bin', _d="SEPA pain message") + class HIDSE2(DebitResponseBase): """Einreichung terminierter SEPA-Einzellastschrift bestätigen, version 2 @@ -65,7 +67,6 @@ class HIDSES2(ParameterSegment): parameter = DataElementGroupField(type=ScheduledDebitParameter2, _d="Parameter terminierte SEPA-Sammellastschrift einreichen") - class HKDME1(BatchDebitBase): """Einreichung terminierter SEPA-Sammellastschrift, version 1 @@ -85,8 +86,6 @@ class HIDMES1(ParameterSegment): parameter = DataElementGroupField(type=ScheduledBatchDebitParameter1, _d="Parameter terminierte SEPA-Sammellastschrift einreichen") - - class HKDME2(BatchDebitBase): """Einreichung terminierter SEPA-Sammellastschrift, version 2 @@ -106,7 +105,6 @@ class HIDMES2(ParameterSegment): parameter = DataElementGroupField(type=ScheduledBatchDebitParameter2, _d="Parameter terminierte SEPA-Sammellastschrift einreichen") - class HKDSC1(FinTS3Segment): """Terminierte SEPA-COR1-Einzellastschrift einreichen, version 1 @@ -129,7 +127,6 @@ class HIDSCS1(ParameterSegment): parameter = DataElementGroupField(type=ScheduledCOR1DebitParameter1, _d="Parameter terminierte SEPA-COR1-Einzellastschrift") - class HKDMC1(BatchDebitBase): """Terminierte SEPA-COR1-Sammellastschrift einreichen, version 1 @@ -149,7 +146,6 @@ class HIDMCS1(ParameterSegment): parameter = DataElementGroupField(type=ScheduledCOR1BatchDebitParameter1, _d="Parameter terminierte SEPA-COR1-Sammellastschrift") - class HKDBS1(FinTS3Segment): """Bestand terminierter SEPA-Einzellastschriften anfordern, version 1 @@ -180,7 +176,6 @@ class HIDBSS1(ParameterSegment): parameter = DataElementGroupField(type=QueryScheduledDebitParameter1, _d="Parameter Bestand terminierter SEPA-Einzellastschriften") - class HKDBS2(FinTS3Segment): """Bestand terminierter SEPA-Einzellastschriften anfordern, version 2 @@ -205,6 +200,7 @@ class HIDBS2(FinTS3Segment): task_changeable = DataElementField(type='jn', required=False, _d="Auftrag änderbar") status_sepa_task = CodeField(enum=StatusSEPATask1, _d="Status SEPA-Auftrag") + class HIDBSS2(ParameterSegment): """Bestand terminierter SEPA-Einzellastschriften Parameter, version 2 @@ -212,7 +208,6 @@ class HIDBSS2(ParameterSegment): parameter = DataElementGroupField(type=QueryScheduledDebitParameter2, _d="Parameter Bestand terminierter SEPA-Einzellastschriften") - class HKDMB1(FinTS3Segment): """Bestand terminierter SEPA-Sammellastschriften anfordern, version 1 @@ -235,6 +230,7 @@ class HIDMB1(FinTS3Segment): debit_count = DataElementField(type='num', max_length=6, _d="Anzahl der Aufträge") sum_amount = DataElementGroupField(type=Amount1, _d="Summe der Beträge") + class HIDMBS1(ParameterSegment): """Bestand terminierter SEPA-Sammellastschriften Parameter, version 1 diff --git a/fints/segments/dialog.py b/fints/segments/dialog.py index 0e34f3d..2227914 100644 --- a/fints/segments/dialog.py +++ b/fints/segments/dialog.py @@ -9,23 +9,27 @@ class HKSYN3(FinTS3Segment): Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals""" synchronisation_mode = CodeField(enum=SynchronisationMode, length=1) + class HISYN4(FinTS3Segment): - "Synchronisierungsantwort" + """Synchronisierungsantwort""" system_id = DataElementField(type='id', _d="Kundensystem-ID") message_number = DataElementField(type='num', max_length=4, required=False, _d="Nachrichtennummer") security_reference_signature_key = DataElementField(type='num', max_length=16, required=False, _d="Sicherheitsreferenznummer für Signierschlüssel") security_reference_digital_signature = DataElementField(type='num', max_length=16, required=False, _d="Sicherheitsreferenznummer für Digitale Signatur") + class HKEND1(FinTS3Segment): """Dialogende, version 1 Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Formals""" dialogue_id = DataElementField(type='id', _d="Dialog-ID") + class HIRMG2(FinTS3Segment): - "Rückmeldungen zur Gesamtnachricht" + """Rückmeldungen zur Gesamtnachricht""" responses = DataElementGroupField(type=Response, min_count=1, max_count=99, _d="Rückmeldung") + class HIRMS2(FinTS3Segment): - "Rückmeldungen zu Segmenten" + """Rückmeldungen zu Segmenten""" responses = DataElementGroupField(type=Response, min_count=1, max_count=99, _d="Rückmeldung") diff --git a/fints/segments/journal.py b/fints/segments/journal.py index 83252de..fdec865 100644 --- a/fints/segments/journal.py +++ b/fints/segments/journal.py @@ -13,6 +13,7 @@ class HKPRO3(FinTS3Segment): max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge") touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt") + class HIPRO3(FinTS3Segment): """Statusprotokoll rückmelden, version 3 @@ -23,6 +24,7 @@ class HIPRO3(FinTS3Segment): time = DataElementField(type='tim', _d="Uhrzeit") responses = DataElementGroupField(type=Response, _d="Rückmeldung") + class HIPROS3(ParameterSegment_22): """Statusprotokoll Parameter, version 3 @@ -38,6 +40,7 @@ class HKPRO4(FinTS3Segment): max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge") touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt") + class HIPRO4(FinTS3Segment): """Statusprotokoll rückmelden, version 4 @@ -48,6 +51,7 @@ class HIPRO4(FinTS3Segment): time = DataElementField(type='tim', _d="Uhrzeit") responses = DataElementGroupField(type=Response, _d="Rückmeldung") + class HIPROS4(ParameterSegment): """Statusprotokoll Parameter, version 4 diff --git a/fints/segments/message.py b/fints/segments/message.py index fc7fc68..6c1d017 100644 --- a/fints/segments/message.py +++ b/fints/segments/message.py @@ -13,17 +13,19 @@ from .base import FinTS3Segment class HNHBK3(FinTS3Segment): - "Nachrichtenkopf" + """Nachrichtenkopf""" message_size = ZeroPaddedNumericField(length=12, _d="Größe der Nachricht (nach Verschlüsselung und Komprimierung)") hbci_version = DataElementField(type='num', max_length=3, _d="HBCI-Version") dialogue_id = DataElementField(type='id', _d="Dialog-ID") message_number = DataElementField(type='num', max_length=4, _d="Nachrichtennummer") reference_message = DataElementGroupField(type=ReferenceMessage, required=False, _d="Bezugsnachricht") + class HNHBS1(FinTS3Segment): - "Nachrichtenabschluss" + """Nachrichtenabschluss""" message_number = DataElementField(type='num', max_length=4, _d="Nachrichtennummer") + class HNVSK3(FinTS3Segment): """Verschlüsselungskopf, version 3 @@ -44,6 +46,7 @@ class HNVSD1(FinTS3Segment): Source: FinTS Financial Transaction Services, Sicherheitsverfahren HBCI""" data = SegmentSequenceField(_d="Daten, verschlüsselt") + class HNSHK4(FinTS3Segment): """Signaturkopf, version 4 @@ -61,6 +64,7 @@ class HNSHK4(FinTS3Segment): key_name = DataElementGroupField(type=KeyName, _d="Schlüsselname") certificate = DataElementGroupField(type=Certificate, required=False, _d="Zertifikat") + class HNSHA2(FinTS3Segment): """Signaturabschluss, version 2 diff --git a/fints/segments/saldo.py b/fints/segments/saldo.py index e94ccb5..74e841c 100644 --- a/fints/segments/saldo.py +++ b/fints/segments/saldo.py @@ -15,6 +15,7 @@ class HKSAL5(FinTS3Segment): max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge") touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt") + class HISAL5(FinTS3Segment): """Saldenrückmeldung, version 5 @@ -31,6 +32,7 @@ class HISAL5(FinTS3Segment): booking_time = DataElementField(type='tim', required=False, _d="Buchungsuhrzeit des Saldos") date_due = DataElementField(type='dat', required=False, _d="Fälligkeit") + class HKSAL6(FinTS3Segment): """Saldenabfrage, version 6 @@ -40,6 +42,7 @@ class HKSAL6(FinTS3Segment): max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge") touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt") + class HISAL6(FinTS3Segment): """Saldenrückmeldung, version 6 @@ -56,6 +59,7 @@ class HISAL6(FinTS3Segment): booking_timestamp = DataElementGroupField(type=Timestamp1, required=False, _d="Buchungszeitpunkt") date_due = DataElementField(type='dat', required=False, _d="Fälligkeit") + class HKSAL7(FinTS3Segment): """Saldenabfrage, version 7 diff --git a/fints/segments/statement.py b/fints/segments/statement.py index c479a04..9fa6b5e 100644 --- a/fints/segments/statement.py +++ b/fints/segments/statement.py @@ -23,6 +23,7 @@ class HIKAZ5(FinTS3Segment): statement_booked = DataElementField(type='bin', _d="Gebuchte Umsätze") statement_pending = DataElementField(type='bin', required=False, _d="Nicht gebuchte Umsätze") + class HKKAZ6(FinTS3Segment): """Kontoumsätze anfordern/Zeitraum, version 6 @@ -42,6 +43,7 @@ class HIKAZ6(FinTS3Segment): statement_booked = DataElementField(type='bin', _d="Gebuchte Umsätze") statement_pending = DataElementField(type='bin', required=False, _d="Nicht gebuchte Umsätze") + class HKKAZ7(FinTS3Segment): """Kontoumsätze anfordern/Zeitraum, version 7 diff --git a/fints/segments/transfer.py b/fints/segments/transfer.py index 3bc6e3f..5203379 100644 --- a/fints/segments/transfer.py +++ b/fints/segments/transfer.py @@ -12,6 +12,7 @@ class HKCCS1(FinTS3Segment): sepa_descriptor = DataElementField(type='an', max_length=256, _d="SEPA Descriptor") sepa_pain_message = DataElementField(type='bin', _d="SEPA pain message") + class HKCCM1(FinTS3Segment): """SEPA-Sammelüberweisung, version 1 @@ -22,6 +23,7 @@ class HKCCM1(FinTS3Segment): sepa_descriptor = DataElementField(type='an', max_length=256, _d="SEPA Descriptor") sepa_pain_message = DataElementField(type='bin', _d="SEPA pain message") + class HICCMS1(ParameterSegment): """SEPA-Sammelüberweisung Parameter, version 1 diff --git a/fints/types.py b/fints/types.py index dca8d8a..5a879ca 100644 --- a/fints/types.py +++ b/fints/types.py @@ -86,6 +86,7 @@ class Field: return " # {}".format(d) return "" + class TypedField(Field, SubclassesMixin): def __new__(cls, *args, **kwargs): target_cls = None @@ -203,6 +204,7 @@ class ValueList: stream.write( (prefix + (level+1)*indent) + "# {} empty items skipped\n".format(skipped_items) ) stream.write( (prefix + level*indent) + "]{}\n".format(trailer) ) + class SegmentSequence: """A sequence of FinTS3Segment objects""" diff --git a/fints/utils.py b/fints/utils.py index d408c07..d3b0073 100644 --- a/fints/utils.py +++ b/fints/utils.py @@ -25,7 +25,6 @@ def print_segments(message): print(u"{}: {}".format(idx, seg.encode('utf-8'))) - def fints_unescape(content): return content.replace('??', '?').replace("?'", "'").replace('?+', '+').replace('?:', ':').replace('?@', '@') @@ -292,6 +291,7 @@ class Password(str): def replace(self, *args, **kwargs): return self.__str__().replace(*args, **kwargs) + class RepresentableEnum(Enum): def __init__(self, *args, **kwargs): Enum.__init__(self) diff --git a/tests/test_client.py b/tests/test_client.py index f2cb794..80344a3 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -3,6 +3,7 @@ from fints.exceptions import FinTSClientPINError from decimal import Decimal import pytest + @pytest.fixture def fints_client(fints_server): return FinTS3PinTanClient( @@ -12,18 +13,21 @@ def fints_client(fints_server): fints_server ) + def test_get_sepa_accounts(fints_client): with fints_client: accounts = fints_client.get_sepa_accounts() assert accounts + def test_get_information(fints_client): with fints_client: information = fints_client.get_information() assert information["bank"]["name"] == 'Test Bank' + def test_pin_wrong(fints_server): client = FinTS3PinTanClient( '12345678', @@ -44,6 +48,7 @@ def test_pin_wrong(fints_server): with pytest.raises(Exception, match="Refusing"): str(client.pin) + def test_resume(fints_client, fints_server): with fints_client: system_id = fints_client.system_id @@ -54,7 +59,7 @@ def test_resume(fints_client, fints_server): c_data = fints_client.get_data(including_private=True) - client2 = FinTS3PinTanClient( + FinTS3PinTanClient( '12345678', 'test1', '1234', @@ -67,6 +72,7 @@ def test_resume(fints_client, fints_server): assert dialog_id == fints_client._standing_dialog.dialogue_id assert fints_client.bpd_version == 78 + def test_transfer_1step(fints_client): with fints_client: accounts = fints_client.get_sepa_accounts() @@ -86,6 +92,7 @@ def test_transfer_1step(fints_client): assert a.responses[0].code == '0010' assert a.responses[0].text == "Transfer 1.23 to DE111234567800000002 re 'Test transfer 1step'" + def test_transfer_1step_regression(fints_client): # Passing a float is not officially supported, but should not fail with wrong values # Decimal(1.23) is Decimal('1.229999999999999982236431605997495353221893310546875') @@ -106,6 +113,7 @@ def test_transfer_1step_regression(fints_client): assert isinstance(a, TransactionResponse) assert a.responses[0].text == "Transfer 1.23 to DE111234567800000002 re 'Test transfer 1step'" + def test_transfer_2step(fints_client): with fints_client: accounts = fints_client.get_sepa_accounts() @@ -125,6 +133,7 @@ def test_transfer_2step(fints_client): assert b.status == ResponseStatus.SUCCESS assert b.responses[0].text == "Transfer 2.34 to DE111234567800000002 re 'Test transfer 2step'" + def test_transfer_2step_continue(fints_client): with fints_client: accounts = fints_client.get_sepa_accounts() @@ -146,6 +155,7 @@ def test_transfer_2step_continue(fints_client): assert b.status == ResponseStatus.SUCCESS assert b.responses[0].text == "Transfer 3.42 to DE111234567800000002 re 'Test transfer 2step'" + def test_tan_wrong(fints_client): with fints_client: accounts = fints_client.get_sepa_accounts() @@ -162,6 +172,7 @@ def test_tan_wrong(fints_client): b = fints_client.send_tan(a, '99881') assert b.status == ResponseStatus.ERROR + def test_tan_hhduc(fints_client): with fints_client: accounts = fints_client.get_sepa_accounts() @@ -182,6 +193,7 @@ def test_tan_hhduc(fints_client): b = fints_client.send_tan(a, flicker.startcode.data) assert b.status == ResponseStatus.SUCCESS + def test_get_transactions(fints_client): with fints_client: accounts = fints_client.get_sepa_accounts() diff --git a/tests/test_formals.py b/tests/test_formals.py index 14b72a7..d479110 100644 --- a/tests/test_formals.py +++ b/tests/test_formals.py @@ -33,6 +33,7 @@ def test_container_simple(): del i2.b assert i2.b is None + def test_container_nested(): class A(Container): a = NumericField() @@ -46,6 +47,7 @@ def test_container_nested(): i1.b.a = '1' assert i1.b.a == 1 + def test_container_count_exact(): class A(Container): a = NumericField(count=2) @@ -83,6 +85,7 @@ def test_container_count_exact(): del i1.a[1] assert i1.a[1] is None + def test_container_count_variable_1(): class A(Container): a = NumericField(min_count=2, max_count=5) @@ -121,6 +124,7 @@ def test_container_count_variable_1(): assert len(i1.a) == 4 + def test_container_count_variable_2(): class A(Container): a = NumericField(min_count=2) @@ -129,6 +133,7 @@ def test_container_count_variable_2(): assert len(i1.a) == 2 + def test_container_count_variable_3(): class A(Container): a = NumericField(max_count=4) @@ -137,6 +142,7 @@ def test_container_count_variable_3(): assert len(i1.a) == 0 + def test_container_count_variable_iter(): class A(Container): a = NumericField(max_count=4) @@ -153,6 +159,7 @@ def test_container_count_variable_iter(): assert len(i1.a) == 3 + def test_container_init(): class A(Container): a = NumericField() @@ -163,6 +170,7 @@ def test_container_init(): assert i1.a == 3 assert i1.b == '04' + def test_container_nested_init(): class A(Container): a = NumericField() @@ -185,6 +193,7 @@ def test_container_nested_init(): i3 = B() assert i3.b.a is None + def test_container_count_init(): class A(Container): a = NumericField(count=4) @@ -196,6 +205,7 @@ def test_container_count_init(): assert len(i1.a) == 4 + def test_container_count_variable_init(): class A(Container): a = NumericField(max_count=4) @@ -218,6 +228,7 @@ def test_container_naive_parse(): assert i1.a == 23 assert i1.b == '42' + def test_invalid_spec(): with pytest.raises(ValueError): class A(Container): @@ -233,6 +244,7 @@ def test_invalid_spec(): A(a=123) + def test_parse_restrictions(): class A(Container): a = NumericField(min_length=2, max_length=3) @@ -252,6 +264,7 @@ def test_parse_restrictions(): with pytest.raises(ValueError, match='min_length=2 not reached'): A(a=1) + def test_unset(): class A(Container): a = NumericField() @@ -268,11 +281,13 @@ def test_unset(): assert B(b=A()).is_unset() assert not B(c=1).is_unset() + def test_sequence_repr(): s = SegmentSequence() assert repr(s) == 'fints.types.SegmentSequence([])' + def test_valuelist_repr(): class A(Container): a = NumericField(max_count=3) @@ -280,6 +295,7 @@ def test_valuelist_repr(): i1 = A() assert repr(i1.a) == "[]" + def test_empty_list(): class A(Container): a = NumericField(max_count=3) @@ -290,11 +306,13 @@ def test_empty_list(): i2 = A(a=[None]) assert len(i2.a) == 0 + def test_segmentheader_short(): h = SegmentHeader('HNHBS', 5, 1) assert repr(h) == "fints.formals.SegmentHeader('HNHBS', 5, 1)" + def test_container_generic(): class A(Container): a = DataElementGroupField() @@ -309,6 +327,7 @@ def test_container_generic(): with pytest.warns(UserWarning, match=r'Generic field used for type None value \[\]'): i2 = A(a=[]) + def test_find_1(): from conftest import TEST_MESSAGES from fints.parser import FinTS3Parser @@ -317,21 +336,22 @@ def test_find_1(): m = FinTS3Parser().parse_message(TEST_MESSAGES['basic_complicated']) assert len(list(m.find_segments('HNHBK'))) == 1 - assert len(list(m.find_segments( ['HNHBK', 'HNHBS'] ))) == 2 + assert len(list(m.find_segments(['HNHBK', 'HNHBS']))) == 2 - assert len(list(m.find_segments( ['HNHBK', 'HNHBS'], 1))) == 1 - assert len(list(m.find_segments( ['HNHBK', 'HNHBS'], (1, 3) ))) == 2 + assert len(list(m.find_segments(['HNHBK', 'HNHBS'], 1))) == 1 + assert len(list(m.find_segments(['HNHBK', 'HNHBS'], (1, 3)))) == 2 assert isinstance(m.find_segment_first('HNHBK'), HNHBK3) assert isinstance(m.find_segment_first('HNHBS'), HNHBS1) assert m.find_segment_first('ITST') is None - assert len( m.find_segment_first('HITANS', 1).parameter.twostep_parameters ) == 2 - assert len( m.find_segment_first('HITANS', 3).parameter.twostep_parameters ) == 6 + assert len(m.find_segment_first('HITANS', 1).parameter.twostep_parameters) == 2 + assert len(m.find_segment_first('HITANS', 3).parameter.twostep_parameters) == 6 assert m.find_segment_first('HITANS', recurse=False) is None + def test_find_by_class(): from conftest import TEST_MESSAGES from fints.parser import FinTS3Parser diff --git a/tests/test_message_parser.py b/tests/test_message_parser.py index 7cb9979..71ac547 100644 --- a/tests/test_message_parser.py +++ b/tests/test_message_parser.py @@ -11,6 +11,7 @@ def test_explode(input_name): if input_name.startswith('basic_'): assert len(segments) == 4 + @pytest.mark.parametrize("input_name", [k for k in TEST_MESSAGES.keys() if k.startswith('basic_')]) def test_parse_basic(input_name): m = FinTS3Parser().parse_message(TEST_MESSAGES[input_name]) @@ -18,12 +19,14 @@ def test_parse_basic(input_name): assert m.segments[3].__class__.__name__ == "HNHBS1" m.print_nested() + @pytest.mark.parametrize("input_name", [k for k in TEST_MESSAGES.keys() if not k.startswith('basic_')]) def test_parse_other(input_name): m = FinTS3Parser().parse_message(TEST_MESSAGES[input_name]) assert isinstance(m, SegmentSequence) m.print_nested() + def test_parse_counted(): from fints.segments.base import FinTS3Segment from fints.formals import NumericField, Container, ContainerField @@ -57,7 +60,6 @@ def test_parse_counted(): class ITST3(FinTS3Segment): b = ContainerField(type=InnerTest, max_count=99) - m5 = FinTS3Parser().parse_message(b"ITST:1:3+12:42+345+61:62:63'") m5.print_nested() assert m5.segments[0].b[0].a[0] == 12 @@ -72,13 +74,14 @@ def test_parse_counted(): def test_parse_HIRMG2(): d = b"HIRMG:3:2+0010::Nachricht entgegengenommen.+0100::Dialog beendet.'" m = FinTS3Parser().parse_message(d) - + seg = m.segments[0] assert seg.header.type == 'HIRMG' assert seg.responses[0].code == '0010' assert seg.responses[1].code == '0100' assert len(seg.responses) == 2 + # Regression test, bug found in the wild def test_extra_colon(): message1 = rb"""HIRMG:2:2:+3060::Teilweise liegen Warnungen/Hinweise vor.'""" @@ -90,6 +93,7 @@ def test_extra_colon(): assert seg.header.version == 2 assert seg.header.reference is None + def test_invalid(): message1 = rb"""12""" @@ -120,6 +124,7 @@ def test_invalid(): with pytest.raises(FinTSParserError, match='^Required field'): m = FinTS3Parser().parse_message(message6) + def test_robust_mode(mock): mock.patch('fints.parser.robust_mode', True) diff --git a/tests/test_message_serializer.py b/tests/test_message_serializer.py index ee8f60e..8640c1b 100644 --- a/tests/test_message_serializer.py +++ b/tests/test_message_serializer.py @@ -9,13 +9,14 @@ def test_serialize_1(): class ITST1(FinTS3Segment): a = NumericField(count=3) - i1 = ITST1(a=[1,3]) + i1 = ITST1(a=[1, 3]) i1.header.number = 42 serialization = rb"""ITST:42:1+1+3+'""" assert FinTS3Serializer().serialize_message(i1) == serialization + def test_implode_1(): m = [ [ @@ -41,6 +42,7 @@ def test_implode_1(): assert FinTS3Parser.explode_segments(s) == m + def test_implode_shorten(): m = [ [ @@ -68,6 +70,7 @@ def test_implode_roundtrip_simple(): message = FinTS3Parser().parse_message(segments) assert FinTS3Serializer().serialize_message(message) == TEST_MESSAGES['basic_simple'] + def test_escape(): assert b"a" == FinTS3Serializer.escape_value('a') @@ -80,13 +83,49 @@ def test_escape(): with pytest.raises(TypeError): FinTS3Serializer.escape_value(1) + def test_serialize_2(): from fints.formals import SegmentSequence import fints.formals, fints.segments - s = SegmentSequence([fints.segments.message.HNHBK3(header=fints.formals.SegmentHeader('HNHBK', 1, 3), message_size='000000000428', hbci_version=300, dialogue_id='430711670077=043999659571CN9D=', message_number=2, reference_message=fints.formals.ReferenceMessage(dialogue_id='430711670077=043999659571CN9D=', message_number=2)), fints.segments.message.HNVSK3(header=fints.formals.SegmentHeader('HNVSK', 998, 3), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='998', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_datetime=fints.formals.SecurityDateTime(date_time_type='1'), encryption_algorithm=fints.formals.EncryptionAlgorithm(usage_encryption='2', operation_mode='2', encryption_algorithm='13', algorithm_parameter_value=b'00000000', algorithm_parameter_name='5', algorithm_parameter_iv_name='1'), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0), compression_function='0'), fints.segments.message.HNVSD1(header=fints.formals.SegmentHeader('HNVSD', 999, 1), data=SegmentSequence([fints.segments.message.HNSHK4(header=fints.formals.SegmentHeader('HNSHK', 2, 4), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='999', security_reference='9166926', security_application_area='1', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_reference_number=1, security_datetime=fints.formals.SecurityDateTime(date_time_type='1'), hash_algorithm=fints.formals.HashAlgorithm(usage_hash='1', hash_algorithm='999', algorithm_parameter_name='1'), signature_algorithm=fints.formals.SignatureAlgorithm(usage_signature='6', signature_algorithm='10', operation_mode='16'), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0)), fints.segments.dialog.HIRMG2(header=fints.formals.SegmentHeader('HIRMG', 3, 2), responses=[fints.formals.Response(code='0010', reference_element=None, text='Nachricht entgegengenommen.'), fints.formals.Response(code='0100', reference_element=None, text='Dialog beendet.')]), fints.segments.message.HNSHA2(header=fints.formals.SegmentHeader('HNSHA', 4, 2), security_reference='9166926')])), fints.segments.message.HNHBS1(header=fints.formals.SegmentHeader('HNHBS', 5, 1), message_number=2)]) - + s = SegmentSequence([fints.segments.message.HNHBK3(header=fints.formals.SegmentHeader('HNHBK', 1, 3), message_size='000000000428', hbci_version=300, + dialogue_id='430711670077=043999659571CN9D=', message_number=2, + reference_message=fints.formals.ReferenceMessage(dialogue_id='430711670077=043999659571CN9D=', + message_number=2)), + fints.segments.message.HNVSK3(header=fints.formals.SegmentHeader('HNVSK', 998, 3), + security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), + security_function='998', security_role='1', + security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', + cid=None, + identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), + security_datetime=fints.formals.SecurityDateTime(date_time_type='1'), + encryption_algorithm=fints.formals.EncryptionAlgorithm(usage_encryption='2', operation_mode='2', + encryption_algorithm='13', + algorithm_parameter_value=b'00000000', + algorithm_parameter_name='5', + algorithm_parameter_iv_name='1'), + key_name=fints.formals.KeyName( + bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), + user_id='hermes', key_type='S', key_number=0, key_version=0), compression_function='0'), + fints.segments.message.HNVSD1(header=fints.formals.SegmentHeader('HNVSD', 999, 1), data=SegmentSequence([fints.segments.message.HNSHK4( + header=fints.formals.SegmentHeader('HNSHK', 2, 4), + security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='999', + security_reference='9166926', security_application_area='1', security_role='1', + security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, + identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), + security_reference_number=1, security_datetime=fints.formals.SecurityDateTime(date_time_type='1'), + hash_algorithm=fints.formals.HashAlgorithm(usage_hash='1', hash_algorithm='999', algorithm_parameter_name='1'), + signature_algorithm=fints.formals.SignatureAlgorithm(usage_signature='6', signature_algorithm='10', operation_mode='16'), + key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), + user_id='hermes', key_type='S', key_number=0, key_version=0)), fints.segments.dialog.HIRMG2( + header=fints.formals.SegmentHeader('HIRMG', 3, 2), + responses=[fints.formals.Response(code='0010', reference_element=None, text='Nachricht entgegengenommen.'), + fints.formals.Response(code='0100', reference_element=None, text='Dialog beendet.')]), fints.segments.message.HNSHA2( + header=fints.formals.SegmentHeader('HNSHA', 4, 2), security_reference='9166926')])), + fints.segments.message.HNHBS1(header=fints.formals.SegmentHeader('HNHBS', 5, 1), message_number=2)]) + assert FinTS3Serializer().serialize_message(s) == TEST_MESSAGES['basic_simple'] + def test_serialize_roundtrips_complex_1(): from fints.formals import SegmentSequence m1 = SegmentSequence(TEST_MESSAGES['basic_complicated']) diff --git a/tests/test_models.py b/tests/test_models.py index 05e4d39..e9922ba 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -15,6 +15,7 @@ def test_metaclass_foo(): assert list(a._fields) == ['header', 'message_size', 'hbci_version', 'dialogue_id', 'message_number', 'reference_message'] assert a._fields['header'] + def test_fints3_only_de_and_deg(): from fints.formals import Field, Container, DataElementGroupField @@ -33,6 +34,7 @@ def test_fints3_only_de_and_deg(): class Foo(FinTS3Segment): c = DataElementGroupField(type=B) + def test_segment_subclassing(): class Base1(FinTS3Segment): a = NumericField() @@ -43,7 +45,8 @@ def test_segment_subclassing(): class ISUBTST1(Base2): c = NumericField() - assert list( ISUBTST1._fields.keys() ) == ['header', 'a', 'b', 'c'] + assert list(ISUBTST1._fields.keys()) == ['header', 'a', 'b', 'c'] + def test_descriptor_subclassing(): a = DataElementField(type='an') @@ -55,23 +58,28 @@ def test_descriptor_subclassing(): c = DataElementField(type='fnord') assert isinstance(c, GenericField) + def test_descriptor_in_use(): assert isinstance(HNHBS1._fields['message_number'], NumericField) + def test_data_element_constructor(): a = HNHBS1(message_number='4') assert a.message_number == 4 + def test_fints3_constructor_auto_segmentheader(): a = HNHBS1(7) assert a.message_number == 7 + def test_data_element_constructor_double_arg(): with pytest.raises(TypeError): HNHBS1(8, message_number='9') + def test_data_element_setgetdel(): a = HNHBS1() @@ -84,6 +92,7 @@ def test_data_element_setgetdel(): del a.message_number assert not a.message_number + def test_descriptors_independent(): a = HNHBK3() b = HNHBK3() @@ -93,6 +102,7 @@ def test_descriptors_independent(): assert a.hbci_version assert not b.hbci_version + def test_parse_container(): a = ['HNHBS', '2', '1', ] @@ -102,12 +112,14 @@ def test_parse_container(): assert h.version == 1 assert h.reference is None + def test_segment_class_type_version(): assert HNHBS1.TYPE == 'HNHBS' assert HNHBS1.VERSION == 1 assert HNHBK3.VERSION == 3 + def test_find_subclass(): a = [ ['HNHBS', '2', '1', ], @@ -117,10 +129,90 @@ def test_find_subclass(): clazz = FinTS3Segment.find_subclass(a) assert clazz is HNHBS1 + def test_nested_output_evalable(): import fints.segments, fints.formals - a = SegmentSequence([fints.segments.message.HNHBK3(header=fints.formals.SegmentHeader('HNHBK', 1, 3, None), message_size='000000000428', hbci_version=300, dialogue_id='430711670077=043999659571CN9D=', message_number=2, reference_message=fints.formals.ReferenceMessage(dialogue_id='430711670077=043999659571CN9D=', message_number=2)), fints.segments.message.HNVSK3(header=fints.formals.SegmentHeader('HNVSK', 998, 3, None), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='998', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_datetime=fints.formals.SecurityDateTime(date_time_type='1', date=None, time=None), encryption_algorithm=fints.formals.EncryptionAlgorithm(usage_encryption='2', operation_mode='2', encryption_algorithm='13', algorithm_parameter_value=b'00000000', algorithm_parameter_name='5', algorithm_parameter_iv_name='1', algorithm_parameter_iv_value=None), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0), compression_function='0', certificate=fints.formals.Certificate(certificate_type=None, certificate_content=None)), fints.segments.message.HNVSD1(header=fints.formals.SegmentHeader('HNVSD', 999, 1, None), data=SegmentSequence([fints.segments.message.HNSHK4(header=fints.formals.SegmentHeader('HNSHK', 2, 4, None), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='999', security_reference='9166926', security_application_area='1', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_reference_number=1, security_datetime=fints.formals.SecurityDateTime(date_time_type='1', date=None, time=None), hash_algorithm=fints.formals.HashAlgorithm(usage_hash='1', hash_algorithm='999', algorithm_parameter_name='1', algorithm_parameter_value=None), signature_algorithm=fints.formals.SignatureAlgorithm(usage_signature='6', signature_algorithm='10', operation_mode='16'), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0), certificate=fints.formals.Certificate(certificate_type=None, certificate_content=None)), fints.segments.base.FinTS3Segment(header=fints.formals.SegmentHeader('HIRMG', 3, 2, None), _additional_data=[['0010', None, 'Nachricht entgegengenommen.'], ['0100', None, 'Dialog beendet.']]), fints.segments.base.FinTS3Segment(header=fints.formals.SegmentHeader('HNSHA', 4, 2, None), _additional_data=['9166926'])])), fints.segments.message.HNHBS1(header=fints.formals.SegmentHeader('HNHBS', 5, 1, None), message_number=2)]) + a = SegmentSequence([fints.segments.message.HNHBK3(header=fints.formals.SegmentHeader('HNHBK', 1, 3, None), message_size='000000000428', hbci_version=300, + dialogue_id='430711670077=043999659571CN9D=', message_number=2, + reference_message=fints.formals.ReferenceMessage(dialogue_id='430711670077=043999659571CN9D=', + message_number=2)), + fints.segments.message.HNVSK3(header=fints.formals.SegmentHeader('HNVSK', 998, 3, None), + security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), + security_function='998', security_role='1', + security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', + cid=None, + identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), + security_datetime=fints.formals.SecurityDateTime(date_time_type='1', date=None, time=None), + encryption_algorithm=fints.formals.EncryptionAlgorithm(usage_encryption='2', operation_mode='2', + encryption_algorithm='13', + algorithm_parameter_value=b'00000000', + algorithm_parameter_name='5', + algorithm_parameter_iv_name='1', + algorithm_parameter_iv_value=None), + key_name=fints.formals.KeyName( + bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), + user_id='hermes', key_type='S', key_number=0, key_version=0), compression_function='0', + certificate=fints.formals.Certificate(certificate_type=None, certificate_content=None)), + fints.segments.message.HNVSD1(header=fints.formals.SegmentHeader('HNVSD', 999, 1, None), data=SegmentSequence([ + fints.segments.message.HNSHK4( + header=fints.formals.SegmentHeader( + 'HNSHK', 2, + 4, None), + security_profile=fints.formals.SecurityProfile( + security_method='PIN', + security_method_version=1), + security_function='999', + security_reference='9166926', + security_application_area='1', + security_role='1', + security_identification_details=fints.formals.SecurityIdentificationDetails( + identified_role='2', + cid=None, + identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), + security_reference_number=1, + security_datetime=fints.formals.SecurityDateTime( + date_time_type='1', + date=None, + time=None), + hash_algorithm=fints.formals.HashAlgorithm( + usage_hash='1', + hash_algorithm='999', + algorithm_parameter_name='1', + algorithm_parameter_value=None), + signature_algorithm=fints.formals.SignatureAlgorithm( + usage_signature='6', + signature_algorithm='10', + operation_mode='16'), + key_name=fints.formals.KeyName( + bank_identifier=fints.formals.BankIdentifier( + country_identifier='280', + bank_code='15050500'), + user_id='hermes', + key_type='S', + key_number=0, + key_version=0), + certificate=fints.formals.Certificate( + certificate_type=None, + certificate_content=None)), + fints.segments.base.FinTS3Segment( + header=fints.formals.SegmentHeader( + 'HIRMG', 3, + 2, None), + _additional_data=[ + ['0010', + None, + 'Nachricht entgegengenommen.'], + ['0100', + None, + 'Dialog beendet.']]), + fints.segments.base.FinTS3Segment( + header=fints.formals.SegmentHeader( + 'HNSHA', 4, + 2, None), + _additional_data=[ + '9166926'])])), + fints.segments.message.HNHBS1(header=fints.formals.SegmentHeader('HNHBS', 5, 1, None), message_number=2)]) output = StringIO() a.print_nested(stream=output)