From b07e84b31cb58c4a3da960d7b5ac01f9cf7801bc Mon Sep 17 00:00:00 2001 From: dankito Date: Wed, 12 Aug 2020 11:26:25 +0200 Subject: [PATCH] Implemented retrieving user's TAN procedures with a non-strong authenticated dialog init with one step TAN procedure (the only process where one step TAN procedure is still allowed) as some banks like Postbank require this --- .../net/dankito/banking/fints/FinTsClient.kt | 46 +++++++++++-------- .../VersionDesSicherheitsverfahrens.kt | 20 +++++++- .../implementierte/PinTanSignaturkopf.kt | 6 +-- .../PinTanVerschluesselungskopf.kt | 1 + .../segmente/implementierte/Signaturkopf.kt | 6 ++- .../implementierte/Verschluesselungskopf.kt | 3 +- .../banking/fints/model/DialogContext.kt | 4 +- .../banking/fints/model/MessageBaseData.kt | 5 +- 8 files changed, 64 insertions(+), 27 deletions(-) diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt index 066148a0..5b13834c 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt @@ -8,10 +8,8 @@ import net.dankito.banking.fints.messages.MessageBuilderResult import net.dankito.banking.fints.messages.datenelemente.implementierte.Dialogsprache import net.dankito.banking.fints.messages.datenelemente.implementierte.KundensystemStatusWerte import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMedienArtVersion -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.TanMediumKlasse -import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.ZkaTanProcedure +import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.VersionDesSicherheitsverfahrens +import net.dankito.banking.fints.messages.datenelemente.implementierte.tan.* import net.dankito.banking.fints.model.* import net.dankito.banking.fints.response.GetUserTanProceduresResponse import net.dankito.banking.fints.response.InstituteSegmentId @@ -113,7 +111,7 @@ open class FinTsClient( } - open fun getBankAndCustomerInfoForNewUser(bank: BankData, customer: CustomerData, callback: (AddAccountResponse) -> Unit) { + open fun getUsersTanProcedures(bank: BankData, customer: CustomerData, callback: (AddAccountResponse) -> Unit) { // just to ensure settings are in its initial state and that bank sends use bank parameter (BPD), // user parameter (UPD) and allowed tan procedures for user (therefore the resetSelectedTanProcedure()) bank.resetBpdVersion() @@ -126,18 +124,30 @@ open class FinTsClient( */ customer.resetSelectedTanProcedure() - val dialogContext = DialogContext(bank, customer, product) + // this is the only case where Einschritt-TAN-Verfahren is accepted: to get user's TAN procedures + val dialogContext = DialogContext(bank, customer, product, versionOfSecurityProcedure = VersionDesSicherheitsverfahrens.Version_1) - initDialogAfterSuccessfulChecks(dialogContext) { initDialogResponse -> + val message = messageBuilder.createInitDialogMessage(dialogContext) + + getAndHandleResponseForMessage(message, dialogContext) { response -> closeDialog(dialogContext) - // even though it is required by specification some banks don't support retrieving user's TAN procedure by setting TAN procedure to '999' - if (bankDoesNotSupportRetrievingUsersTanProcedures(initDialogResponse)) { - getBankAndCustomerInfoForNewUserViaAnonymousDialog(bank, customer, callback) - } - else { - callback(AddAccountResponse(initDialogResponse, bank, customer)) - } + handleGetUsersTanProceduresResponse(response, dialogContext, callback) + } + } + + protected open fun handleGetUsersTanProceduresResponse(response: Response, dialogContext: DialogContext, callback: (AddAccountResponse) -> Unit) { + if (response.successful) { // TODO: really update data only on complete successfully response? as it may contain useful information anyway // TODO: extract method for this code part + updateBankData(dialogContext.bank, response) + updateCustomerData(dialogContext.customer, dialogContext.bank, response) + } + + // even though it is required by specification some banks don't support retrieving user's TAN procedure by setting TAN procedure to '999' + if (bankDoesNotSupportRetrievingUsersTanProcedures(response)) { + getBankAndCustomerInfoForNewUserViaAnonymousDialog(dialogContext.bank, dialogContext.customer, callback) // TODO: should not be necessary anymore + } + else { + callback(AddAccountResponse(response, dialogContext.bank, dialogContext.customer)) } } @@ -208,9 +218,9 @@ open class FinTsClient( val originalAreWeThatGentleToCloseDialogs = areWeThatGentleToCloseDialogs areWeThatGentleToCloseDialogs = false - /* First dialog: Get user's basic data like her TAN procedures */ + /* First dialog: Get user's basic data like BPD, customer system ID and her TAN procedures */ - getBankAndCustomerInfoForNewUser(bank, customer) { newUserInfoResponse -> + getUsersTanProcedures(bank, customer) { newUserInfoResponse -> if (newUserInfoResponse.isSuccessful == false) { // bank parameter (FinTS server address, ...) already seem to be wrong callback(newUserInfoResponse) @@ -541,7 +551,7 @@ open class FinTsClient( protected open fun ensureBasicBankDataRetrieved(bank: BankData, customer: CustomerData, callback: (Response) -> Unit) { if (bank.supportedTanProcedures.isEmpty() || bank.supportedJobs.isEmpty()) { - getBankAndCustomerInfoForNewUser(bank, customer) { getBankInfoResponse -> + getUsersTanProcedures(bank, customer) { getBankInfoResponse -> if (getBankInfoResponse.isSuccessful == false || bank.supportedTanProcedures.isEmpty() || bank.supportedJobs.isEmpty()) { @@ -561,7 +571,7 @@ open class FinTsClient( protected open fun ensureTanProcedureIsSelected(bank: BankData, customer: CustomerData, callback: (Response) -> Unit) { if (customer.isTanProcedureSelected == false) { if (customer.supportedTanProcedures.isEmpty()) { - getBankAndCustomerInfoForNewUser(bank, customer) { + getUsersTanProcedures(bank, customer) { if (customer.supportedTanProcedures.isEmpty()) { // could not retrieve supported tan procedures for user callback(Response(false, noTanProcedureSelected = true)) } diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/implementierte/signatur/VersionDesSicherheitsverfahrens.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/implementierte/signatur/VersionDesSicherheitsverfahrens.kt index 7483ef94..9f414d02 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/implementierte/signatur/VersionDesSicherheitsverfahrens.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/implementierte/signatur/VersionDesSicherheitsverfahrens.kt @@ -3,6 +3,17 @@ package net.dankito.banking.fints.messages.datenelemente.implementierte.signatur enum class VersionDesSicherheitsverfahrens(val methodNumber: Int) { + /* + PinTan: + + Version des Sicherheitsverfahrens + „1“ : bei allen Nachrichten, wenn Dialog im Einschritt-Verfahren + „2“ : bei allen Nachrichten, wenn Dialog im Zwei-Schritt-Verfahren + + Die Verwendung des Ein-Schritt-Verfahrens ist jedoch nur noch in bestimmten Situationen, + z. B. zur Ermittlung der zugelassenen Sicherheitsverfahren, zugelassen. + */ + Version_1(1), Version_2(2), @@ -21,6 +32,13 @@ enum class VersionDesSicherheitsverfahrens(val methodNumber: Int) { Version_9(9), - Version_10(10) + Version_10(10); + + + companion object { + + val PinTanDefaultVersion = Version_2 + + } } \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/PinTanSignaturkopf.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/PinTanSignaturkopf.kt index fd00d297..b312d253 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/PinTanSignaturkopf.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/PinTanSignaturkopf.kt @@ -1,9 +1,6 @@ package net.dankito.banking.fints.messages.segmente.implementierte -import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.OperationsmodusKodiert -import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.Schluesselnummer -import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.Schluesselversion -import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.SignaturalgorithmusKodiert +import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.* import net.dankito.banking.fints.model.MessageBaseData @@ -18,6 +15,7 @@ open class PinTanSignaturkopf( segmentNumber, baseData.bank, baseData.customer, + baseData.versionOfSecurityProcedure, securityControlReference, date, time, diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/PinTanVerschluesselungskopf.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/PinTanVerschluesselungskopf.kt index fbea5b9e..ae401d95 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/PinTanVerschluesselungskopf.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/PinTanVerschluesselungskopf.kt @@ -14,6 +14,7 @@ open class PinTanVerschluesselungskopf( ) : Verschluesselungskopf( baseData.bank, baseData.customer, + baseData.versionOfSecurityProcedure, date, time, Operationsmodus.Cipher_Block_Chaining, diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/Signaturkopf.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/Signaturkopf.kt index dab3f968..2422b987 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/Signaturkopf.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/Signaturkopf.kt @@ -26,6 +26,7 @@ open class Signaturkopf( segmentNumber: Int, bank: BankData, customer: CustomerData, + versionOfSecurityProcedure: VersionDesSicherheitsverfahrens, securityControlReference: String, date: Int, time: Int, @@ -36,7 +37,10 @@ open class Signaturkopf( ) : Segment(listOf( Segmentkopf(MessageSegmentId.SignatureHeader, 4, segmentNumber), // allowed - Sicherheitsprofil(Sicherheitsverfahren.PIN_TAN_Verfahren, VersionDesSicherheitsverfahrens.Version_2), // fints4k only supports Pin/Tan and PSD2 requires two step tan procedure + Sicherheitsprofil( + Sicherheitsverfahren.PIN_TAN_Verfahren, + versionOfSecurityProcedure + ), // fints4k only supports Pin/Tan and PSD2 requires two step tan procedure; the only exception is the first dialog to get user's TAN procedures which allows to use one step tan procedure (as we don't know TAN procedures yet) SicherheitsfunktionKodiert(customer.selectedTanProcedure.securityFunction), Sicherheitskontrollreferenz(securityControlReference), // allowed: <>0 BereichDerSicherheitsapplikationKodiert(BereichDerSicherheitsapplikation.SignaturkopfUndHBCINutzdaten), // allowed: 1 ? diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/Verschluesselungskopf.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/Verschluesselungskopf.kt index c8522697..1f27e965 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/Verschluesselungskopf.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/Verschluesselungskopf.kt @@ -36,6 +36,7 @@ import net.dankito.banking.fints.model.CustomerData open class Verschluesselungskopf( bank: BankData, customer: CustomerData, + versionOfSecurityProcedure: VersionDesSicherheitsverfahrens, date: Int, time: Int, mode: Operationsmodus, @@ -47,7 +48,7 @@ open class Verschluesselungskopf( ) : Segment(listOf( Segmentkopf(MessageSegmentId.EncryptionHeader, 3, 998), - Sicherheitsprofil(Sicherheitsverfahren.PIN_TAN_Verfahren, VersionDesSicherheitsverfahrens.Version_2), // fints4k only supports Pin/Tan and PSD2 requires two step tan procedure + Sicherheitsprofil(Sicherheitsverfahren.PIN_TAN_Verfahren, versionOfSecurityProcedure), // fints4k only supports Pin/Tan and PSD2 requires two step tan procedure; the only exception is the first dialog to get user's TAN procedures which allows to use one step tan procedure (as we don't know TAN procedures yet) SicherheitsfunktionKodiert(Sicherheitsfunktion.Klartext), RolleDesSicherheitslieferantenKodiert(), // allowed: 1, 4 SicherheitsidentifikationDetails(customer.customerSystemId), diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/DialogContext.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/DialogContext.kt index f35072ca..ae4617f5 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/DialogContext.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/DialogContext.kt @@ -1,6 +1,7 @@ package net.dankito.banking.fints.model import net.dankito.banking.fints.messages.MessageBuilderResult +import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.VersionDesSicherheitsverfahrens import net.dankito.banking.fints.response.Response @@ -13,9 +14,10 @@ open class DialogContext( var dialogId: String = InitialDialogId, var response: Response? = null, var didBankCloseDialog: Boolean = false, + versionOfSecurityProcedure: VersionDesSicherheitsverfahrens = VersionDesSicherheitsverfahrens.Version_2, // for PinTan almost always the case except for getting a user's TAN procedures var previousMessageInDialog: MessageBuilderResult? = null, var chunkedResponseHandler: ((Response) -> Unit)? = null -) : MessageBaseData(bank, customer, product) { +) : MessageBaseData(bank, customer, product, versionOfSecurityProcedure) { companion object { const val InitialDialogId = "0" diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/MessageBaseData.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/MessageBaseData.kt index fdd24d37..10f00f38 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/MessageBaseData.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/MessageBaseData.kt @@ -1,8 +1,11 @@ package net.dankito.banking.fints.model +import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur.VersionDesSicherheitsverfahrens + open class MessageBaseData( val bank: BankData, val customer: CustomerData, - val product: ProductData + val product: ProductData, + val versionOfSecurityProcedure: VersionDesSicherheitsverfahrens = VersionDesSicherheitsverfahrens.PinTanDefaultVersion ) \ No newline at end of file