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

This commit is contained in:
dankito 2020-08-12 11:26:25 +02:00
parent 639653f430
commit b07e84b31c
8 changed files with 64 additions and 27 deletions

View File

@ -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))
}

View File

@ -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
}
}

View File

@ -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,

View File

@ -14,6 +14,7 @@ open class PinTanVerschluesselungskopf(
) : Verschluesselungskopf(
baseData.bank,
baseData.customer,
baseData.versionOfSecurityProcedure,
date,
time,
Operationsmodus.Cipher_Block_Chaining,

View File

@ -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 ?

View File

@ -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),

View File

@ -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"

View File

@ -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
)