Implemented detecting if wrong credentials have been entered (not 100 % reliable though)

This commit is contained in:
dankito 2020-09-30 02:46:07 +02:00
parent 572f8d81ec
commit 38bb0c90ff
10 changed files with 64 additions and 19 deletions

View File

@ -24,4 +24,5 @@ open class RetrievedAccountData(
}
}
}

View File

@ -27,14 +27,23 @@ open class BankResponse(
open val responseContainsErrors: Boolean
get() = errorMessage == null && messageFeedback?.isError == true
open val wrongCredentialsEntered: Boolean
get() {
val wrongCredentialsEnteredFeedbacks = segmentFeedbacks.flatMap { it.feedbacks }
.filter { it.responseCode in 9910..9949 || (it.responseCode == 9210 && it.message.contains("Unbekannt", true)) } // this is not 100 % correct, there are e.g. messages like "9941 TAN ungültig" or "9910 Chipkarte gesperrt", see p. 22-23 FinTS_Rueckmeldungscodes ->
.filterNot { it.message.contains("TAN", true) || it.message.contains("Chipkarte", true) } // ... try to filter these
return wrongCredentialsEnteredFeedbacks.isNotEmpty()
}
open var tanRequiredButUserDidNotEnterOne = false
open var tanRequiredButWeWereToldToAbortIfSo = false
open val successful: Boolean
get() = noTanMethodSelected == false && couldCreateMessage && didReceiveResponse
&& responseContainsErrors == false && tanRequiredButUserDidNotEnterOne == false
&& tanRequiredButWeWereToldToAbortIfSo == false
&& responseContainsErrors == false && wrongCredentialsEntered == false
&& tanRequiredButUserDidNotEnterOne == false && tanRequiredButWeWereToldToAbortIfSo == false
open val isStrongAuthenticationRequired: Boolean
get() = tanResponse?.isStrongAuthenticationRequired == true

View File

@ -20,6 +20,8 @@ open class FinTsClientResponse(
*/
open val errorMessage: String? = null,
open val wrongCredentialsEntered: Boolean = false,
open val userCancelledAction: Boolean = false,
open val tanRequiredButWeWereToldToAbortIfSo: Boolean = false,
@ -33,7 +35,8 @@ open class FinTsClientResponse(
constructor(response: BankResponse) : this(response.successful, response.noTanMethodSelected,
response.isStrongAuthenticationRequired, response.tanResponse, response.errorsToShowToUser,
response.errorMessage, response.tanRequiredButUserDidNotEnterOne, response.tanRequiredButWeWereToldToAbortIfSo,
response.errorMessage, response.wrongCredentialsEntered,
response.tanRequiredButUserDidNotEnterOne, response.tanRequiredButWeWereToldToAbortIfSo,
response.messageCreationError?.isJobAllowed ?: true,
response.messageCreationError?.isJobVersionSupported ?: true,
response.messageCreationError?.allowedVersions ?: listOf(),

View File

@ -12,4 +12,18 @@ open class RetrievedAccountData(
open val unbookedTransactions: List<Any>,
open val retrievedTransactionsFrom: Date?,
open val retrievedTransactionsTo: Date?
)
) {
companion object {
fun unsuccessful(account: TypedBankAccount): RetrievedAccountData {
return RetrievedAccountData(account, false, null, listOf(), listOf(), null, null)
}
fun unsuccessfulList(account: TypedBankAccount): List<RetrievedAccountData> {
return listOf(unsuccessful(account))
}
}
}

View File

@ -7,8 +7,9 @@ open class AddAccountResponse(
open val bank: TypedBankData,
retrievedData: List<RetrievedAccountData> = listOf(),
errorToShowToUser: String?,
wrongCredentialsEntered: Boolean = false,
userCancelledAction: Boolean = false
) : GetTransactionsResponse(retrievedData, errorToShowToUser, userCancelledAction) {
) : GetTransactionsResponse(retrievedData, errorToShowToUser, wrongCredentialsEntered, userCancelledAction) {
constructor(bank: TypedBankData, errorToShowToUser: String?) : this(bank, listOf(), errorToShowToUser)

View File

@ -4,6 +4,7 @@ package net.dankito.banking.ui.model.responses
open class BankingClientResponse(
open val successful: Boolean,
open val errorToShowToUser: String?,
open val wrongCredentialsEntered: Boolean = false,
open val userCancelledAction: Boolean = false // TODO: not implemented in hbci4jBankingClient yet
) {

View File

@ -7,11 +7,15 @@ import net.dankito.banking.ui.model.TypedBankAccount
open class GetTransactionsResponse(
open val retrievedData: List<RetrievedAccountData>,
errorToShowToUser: String?,
wrongCredentialsEntered: Boolean = false,
userCancelledAction: Boolean = false,
open val tanRequiredButWeWereToldToAbortIfSo: Boolean = false
) : BankingClientResponse(true /* any value */, errorToShowToUser, userCancelledAction) {
) : BankingClientResponse(true /* any value */, errorToShowToUser, wrongCredentialsEntered, userCancelledAction) {
constructor(account: TypedBankAccount, errorToShowToUser: String) : this(listOf(RetrievedAccountData(account, false, null, listOf(), listOf(), null, null)), errorToShowToUser)
constructor(account: TypedBankAccount, errorToShowToUser: String) : this(RetrievedAccountData.unsuccessfulList(account), errorToShowToUser)
constructor(account: TypedBankAccount, response: BankingClientResponse) : this(RetrievedAccountData.unsuccessfulList(account), response.errorToShowToUser,
response.wrongCredentialsEntered, response.userCancelledAction, (response as? GetTransactionsResponse)?.tanRequiredButWeWereToldToAbortIfSo ?: false)
constructor(retrievedData: RetrievedAccountData) : this(listOf(retrievedData))
@ -20,6 +24,7 @@ open class GetTransactionsResponse(
override val successful: Boolean
get() = errorToShowToUser == null
&& wrongCredentialsEntered == false
&& retrievedData.isNotEmpty()
&& retrievedData.none { it.account.supportsRetrievingAccountTransactions && it.successfullyRetrievedData == false }

View File

@ -80,9 +80,14 @@ open class fints4kBankingClient(
override fun getTransactionsAsync(parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) {
val account = parameter.account
findAccountForAccount(account) { accountData, errorMessage ->
findAccountForAccount(account) { accountData, response ->
if (accountData == null) {
callback(GetTransactionsResponse(account, errorMessage ?: ""))
if (response != null) {
callback(GetTransactionsResponse(account, response))
}
else { // should never be the case
callback(GetTransactionsResponse(account, ""))
}
}
else {
val mappedParameter = GetTransactionsParameter(accountData, parameter.alsoRetrieveBalance, parameter.fromDate,
@ -113,9 +118,15 @@ open class fints4kBankingClient(
override fun transferMoneyAsync(data: TransferMoneyData, callback: (BankingClientResponse) -> Unit) {
findAccountForAccount(data.account) { account, errorMessage ->
findAccountForAccount(data.account) { account, response ->
if (account == null) {
callback(BankingClientResponse(false, errorMessage))
if (response != null) {
callback(response)
}
else { // should never be the case
callback(BankingClientResponse(false, "Konnte Kontodaten nicht vom Bankserver abrufen für ${data.account.identifier}. " +
"Besteht eine Netzwerkverbindung und sind der eingegebenen Benutzername und Passwort korrekt?")) // TODO: translate
}
}
else {
val mappedData = BankTransferData(data.recipientName, data.recipientAccountId, data.recipientBankCode, data.amount.toMoney(), data.reference, data.realTimeTransfer)
@ -149,7 +160,7 @@ open class fints4kBankingClient(
}
protected open fun findAccountForAccount(account: TypedBankAccount, findAccountResult: (AccountData?, error: String?) -> Unit) {
protected open fun findAccountForAccount(account: TypedBankAccount, findAccountResult: (AccountData?, BankingClientResponse?) -> Unit) {
val mappedAccount = mapper.findMatchingAccount(fintsBank, account)
if (mappedAccount != null) {
@ -158,10 +169,10 @@ open class fints4kBankingClient(
else { // then try to get account data by fetching data from bank
addAccountAsync { response ->
if (response.successful) {
findAccountResult(mapper.findMatchingAccount(fintsBank, account), response.errorToShowToUser)
findAccountResult(mapper.findMatchingAccount(fintsBank, account), response)
}
else {
findAccountResult(null, response.errorToShowToUser)
findAccountResult(null, response)
}
}
}

View File

@ -24,18 +24,18 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
open fun mapResponse(response: FinTsClientResponse): BankingClientResponse {
return BankingClientResponse(response.successful, mapErrorToShowToUser(response), response.userCancelledAction)
return BankingClientResponse(response.successful, mapErrorToShowToUser(response), response.wrongCredentialsEntered, response.userCancelledAction)
}
open fun mapResponse(bank: TypedBankData, response: net.dankito.banking.fints.response.client.AddAccountResponse): AddAccountResponse {
return AddAccountResponse(bank, map(bank, response.retrievedData), mapErrorToShowToUser(response), response.userCancelledAction)
return AddAccountResponse(bank, map(bank, response.retrievedData), mapErrorToShowToUser(response), response.wrongCredentialsEntered, response.userCancelledAction)
}
open fun mapResponse(account: TypedBankAccount, response: net.dankito.banking.fints.response.client.GetTransactionsResponse): GetTransactionsResponse {
return GetTransactionsResponse(map(account.bank as TypedBankData, response.retrievedData),
mapErrorToShowToUser(response), response.userCancelledAction, response.tanRequiredButWeWereToldToAbortIfSo)
mapErrorToShowToUser(response), response.wrongCredentialsEntered, response.userCancelledAction, response.tanRequiredButWeWereToldToAbortIfSo)
}
open fun map(bank: TypedBankData, retrievedData: List<net.dankito.banking.fints.model.RetrievedAccountData>): List<RetrievedAccountData> {

View File

@ -102,11 +102,11 @@ open class hbci4jBankingClient(
response.retrievedData.first()
}
else {
RetrievedAccountData(account, false, null, listOf(), listOf(), null)
RetrievedAccountData.unsuccessful(account)
}
}
return AddAccountResponse(bank, retrievedData, null, userCancelledAction)
return AddAccountResponse(bank, retrievedData, null, false, userCancelledAction)
}