Added option to control if sensitive data should get removed from messageLog

This commit is contained in:
dankito 2024-08-20 13:32:42 +02:00
parent 0361a0e6b8
commit 9a7615fe88
8 changed files with 27 additions and 20 deletions

View File

@ -53,7 +53,7 @@ open class FinTsClient(
if (getAccountInfoResponse.successful == false || param.retrieveOnlyAccountInfo) { if (getAccountInfoResponse.successful == false || param.retrieveOnlyAccountInfo) {
return GetAccountDataResponse(mapper.mapErrorCode(getAccountInfoResponse), mapper.mapErrorMessages(getAccountInfoResponse), null, return GetAccountDataResponse(mapper.mapErrorCode(getAccountInfoResponse), mapper.mapErrorMessages(getAccountInfoResponse), null,
getAccountInfoResponse.messageLogWithoutSensitiveData, bank) getAccountInfoResponse.messageLog, bank)
} else { } else {
return getAccountData(param, getAccountInfoResponse.bank, getAccountInfoResponse.bank.accounts, getAccountInfoResponse) return getAccountData(param, getAccountInfoResponse.bank, getAccountInfoResponse.bank.accounts, getAccountInfoResponse)
} }
@ -69,7 +69,7 @@ open class FinTsClient(
if (accountsSupportingRetrievingTransactions.isEmpty()) { if (accountsSupportingRetrievingTransactions.isEmpty()) {
val errorMessage = "None of the accounts ${accounts.map { it.productName }} supports retrieving balance or transactions" // TODO: translate val errorMessage = "None of the accounts ${accounts.map { it.productName }} supports retrieving balance or transactions" // TODO: translate
return GetAccountDataResponse(ErrorCode.NoneOfTheAccountsSupportsRetrievingData, errorMessage, mapper.map(bank), previousJobResponse?.messageLogWithoutSensitiveData ?: listOf(), bank) return GetAccountDataResponse(ErrorCode.NoneOfTheAccountsSupportsRetrievingData, errorMessage, mapper.map(bank), previousJobResponse?.messageLog ?: listOf(), bank)
} }
accountsSupportingRetrievingTransactions.forEach { account -> accountsSupportingRetrievingTransactions.forEach { account ->
@ -115,7 +115,7 @@ open class FinTsClient(
if (getAccountInfoResponse.successful == false) { if (getAccountInfoResponse.successful == false) {
return TransferMoneyResponse(mapper.mapErrorCode(getAccountInfoResponse), mapper.mapErrorMessages(getAccountInfoResponse), return TransferMoneyResponse(mapper.mapErrorCode(getAccountInfoResponse), mapper.mapErrorMessages(getAccountInfoResponse),
getAccountInfoResponse.messageLogWithoutSensitiveData, bank) getAccountInfoResponse.messageLog, bank)
} else { } else {
return transferMoneyAsync(param, recipientBankIdentifier, getAccountInfoResponse.bank, getAccountInfoResponse.bank.accounts, getAccountInfoResponse) return transferMoneyAsync(param, recipientBankIdentifier, getAccountInfoResponse.bank, getAccountInfoResponse.bank.accounts, getAccountInfoResponse)
} }
@ -129,14 +129,14 @@ open class FinTsClient(
val accountToUse: AccountData val accountToUse: AccountData
if (accountsSupportingTransfer.isEmpty()) { if (accountsSupportingTransfer.isEmpty()) {
return TransferMoneyResponse(ErrorCode.NoAccountSupportsMoneyTransfer, "None of the accounts $accounts supports money transfer", previousJobResponse?.messageLogWithoutSensitiveData ?: listOf(), bank) return TransferMoneyResponse(ErrorCode.NoAccountSupportsMoneyTransfer, "None of the accounts $accounts supports money transfer", previousJobResponse?.messageLog ?: listOf(), bank)
} else if (accountsSupportingTransfer.size == 1) { } else if (accountsSupportingTransfer.size == 1) {
accountToUse = accountsSupportingTransfer.first() accountToUse = accountsSupportingTransfer.first()
} else { } else {
val selectedAccount = param.selectAccountToUseForTransfer?.invoke(accountsSupportingTransfer) val selectedAccount = param.selectAccountToUseForTransfer?.invoke(accountsSupportingTransfer)
if (selectedAccount == null) { if (selectedAccount == null) {
return TransferMoneyResponse(ErrorCode.MoreThanOneAccountSupportsMoneyTransfer, "More than one of the accounts $accountsSupportingTransfer supports money transfer, so we cannot clearly determine which one to use for this transfer", previousJobResponse?.messageLogWithoutSensitiveData ?: listOf(), bank) return TransferMoneyResponse(ErrorCode.MoreThanOneAccountSupportsMoneyTransfer, "More than one of the accounts $accountsSupportingTransfer supports money transfer, so we cannot clearly determine which one to use for this transfer", previousJobResponse?.messageLog ?: listOf(), bank)
} }
accountToUse = selectedAccount accountToUse = selectedAccount

View File

@ -20,7 +20,7 @@ open class FinTsClientForCustomer(
protected val client = FinTsClientDeprecated(config, callback) protected val client = FinTsClientDeprecated(config, callback)
open val messageLogWithoutSensitiveData: List<MessageLogEntry> = mutableListOf() open val messageLog: List<MessageLogEntry> = mutableListOf()
open suspend fun addAccountAsync(): AddAccountResponse { open suspend fun addAccountAsync(): AddAccountResponse {

View File

@ -3,6 +3,7 @@ package net.dankito.banking.fints.config
import net.dankito.banking.fints.model.ProductData import net.dankito.banking.fints.model.ProductData
data class FinTsClientOptions( data class FinTsClientOptions(
val removeSensitiveDataFromMessageLog: Boolean = true,
val version: String = "1.0.0", // TODO: get version dynamically val version: String = "1.0.0", // TODO: get version dynamically
val productName: String = "15E53C26816138699C7B6A3E8" val productName: String = "15E53C26816138699C7B6A3E8"
) { ) {

View File

@ -1,8 +1,8 @@
package net.dankito.banking.fints.log package net.dankito.banking.fints.log
import net.codinux.log.Logger
import net.codinux.log.LoggerFactory import net.codinux.log.LoggerFactory
import net.codinux.log.logger import net.codinux.log.logger
import net.dankito.banking.fints.config.FinTsClientOptions
import net.dankito.banking.fints.model.BankData import net.dankito.banking.fints.model.BankData
import net.dankito.banking.fints.model.MessageLogEntry import net.dankito.banking.fints.model.MessageLogEntry
import net.dankito.banking.fints.model.MessageLogEntryType import net.dankito.banking.fints.model.MessageLogEntryType
@ -12,7 +12,9 @@ import net.dankito.banking.fints.extensions.toStringWithMinDigits
import kotlin.reflect.KClass import kotlin.reflect.KClass
open class MessageLogCollector { open class MessageLogCollector(
private val options: FinTsClientOptions = FinTsClientOptions()
) {
companion object { companion object {
val FindAccountTransactionsStartRegex = Regex("^HIKAZ:\\d:\\d:\\d\\+@\\d+@", RegexOption.MULTILINE) val FindAccountTransactionsStartRegex = Regex("^HIKAZ:\\d:\\d:\\d\\+@\\d+@", RegexOption.MULTILINE)
@ -26,12 +28,12 @@ open class MessageLogCollector {
} }
protected open val messageLog = ArrayList<MessageLogEntry>() // TODO: make thread safe like with CopyOnWriteArrayList protected open val _messageLog = ArrayList<MessageLogEntry>() // TODO: make thread safe like with CopyOnWriteArrayList
// in either case remove sensitive data after response is parsed as otherwise some information like account holder name and accounts may is not set yet on BankData // in either case remove sensitive data after response is parsed as otherwise some information like account holder name and accounts may is not set yet on BankData
open val messageLogWithoutSensitiveData: List<MessageLogEntry> open val messageLog: List<MessageLogEntry>
// safe CPU cycles by only formatting and removing sensitive data if messageLog is really requested // safe CPU cycles by only formatting and removing sensitive data if messageLog is really requested
get() = messageLog.map { MessageLogEntry(it.type, it.context, it.messageTrace, createMessageForLog(it), it.error, it.time) } get() = _messageLog.map { MessageLogEntry(it.type, it.context, it.messageTrace, createMessageForLog(it), it.error, it.time) }
private fun createMessageForLog(logEntry: MessageLogEntry): String { private fun createMessageForLog(logEntry: MessageLogEntry): String {
val message = if (logEntry.type == MessageLogEntryType.Error) { val message = if (logEntry.type == MessageLogEntryType.Error) {
@ -40,7 +42,11 @@ open class MessageLogCollector {
logEntry.messageTrace + "\n" + prettyPrintHbciMessage(logEntry.message) logEntry.messageTrace + "\n" + prettyPrintHbciMessage(logEntry.message)
} }
return safelyRemoveSensitiveDataFromMessage(message, logEntry.context.bank) return if (options.removeSensitiveDataFromMessageLog) {
safelyRemoveSensitiveDataFromMessage(message, logEntry.context.bank)
} else {
message
}
} }
@ -62,7 +68,7 @@ open class MessageLogCollector {
} }
protected open fun addMessageLogEntry(type: MessageLogEntryType, context: MessageContext, messageTrace: String, message: String, error: Throwable? = null) { protected open fun addMessageLogEntry(type: MessageLogEntryType, context: MessageContext, messageTrace: String, message: String, error: Throwable? = null) {
messageLog.add(MessageLogEntry(type, context, messageTrace, message, error)) _messageLog.add(MessageLogEntry(type, context, messageTrace, message, error))
} }

View File

@ -158,7 +158,7 @@ open class FinTsModelMapper {
} }
open fun mergeMessageLog(vararg responses: FinTsClientResponse?): List<MessageLogEntry> { open fun mergeMessageLog(vararg responses: FinTsClientResponse?): List<MessageLogEntry> {
return responses.filterNotNull().flatMap { it.messageLogWithoutSensitiveData } return responses.filterNotNull().flatMap { it.messageLog }
} }
} }

View File

@ -24,7 +24,7 @@ open class JobContext(
* Only set if the current context is for a specific account (like get account's transactions). * Only set if the current context is for a specific account (like get account's transactions).
*/ */
open val account: AccountData? = null, open val account: AccountData? = null,
protected open val messageLogCollector: MessageLogCollector = MessageLogCollector() protected open val messageLogCollector: MessageLogCollector = MessageLogCollector(config.options)
) : MessageBaseData(bank, config.options.product), IMessageLogAppender { ) : MessageBaseData(bank, config.options.product), IMessageLogAppender {
companion object { companion object {
@ -38,8 +38,8 @@ open class JobContext(
open val responseParser: ResponseParser = ResponseParser(logAppender = this) open val responseParser: ResponseParser = ResponseParser(logAppender = this)
open val messageLogWithoutSensitiveData: List<MessageLogEntry> open val messageLog: List<MessageLogEntry>
get() = messageLogCollector.messageLogWithoutSensitiveData get() = messageLogCollector.messageLog
open var dialog: DialogContext = DialogContext() // create null value so that variable is not null open var dialog: DialogContext = DialogContext() // create null value so that variable is not null

View File

@ -17,7 +17,7 @@ open class FinTsClientResponse(
open val isStrongAuthenticationRequired: Boolean, open val isStrongAuthenticationRequired: Boolean,
open val tanRequired: TanResponse? = null, open val tanRequired: TanResponse? = null,
open val messageLogWithoutSensitiveData: List<MessageLogEntry>, open val messageLog: List<MessageLogEntry>,
/** /**
* A fints4k internal error like an error occurred during web request or response parsing. * A fints4k internal error like an error occurred during web request or response parsing.
@ -43,7 +43,7 @@ open class FinTsClientResponse(
constructor(context: JobContext, response: BankResponse) : this(response.successful, response.didReceiveResponse, response.noTanMethodSelected, constructor(context: JobContext, response: BankResponse) : this(response.successful, response.didReceiveResponse, response.noTanMethodSelected,
response.isStrongAuthenticationRequired, response.tanResponse, context.messageLogWithoutSensitiveData, response.isStrongAuthenticationRequired, response.tanResponse, context.messageLog,
response.internalError, response.errorsToShowToUser, response.isPinLocked, response.wrongCredentialsEntered, response.internalError, response.errorsToShowToUser, response.isPinLocked, response.wrongCredentialsEntered,
response.tanRequiredButUserDidNotEnterOne, response.tanRequiredButWeWereToldToAbortIfSo, response.tanRequiredButUserDidNotEnterOne, response.tanRequiredButWeWereToldToAbortIfSo,
response.messageThatCouldNotBeCreated?.isJobAllowed ?: true, response.messageThatCouldNotBeCreated?.isJobAllowed ?: true,

View File

@ -8,7 +8,7 @@ open class GetTransactionsResponse(
errorMessage: String? = null errorMessage: String? = null
) : FinTsClientResponse(isSuccessful(retrievedResponses), retrievedResponses.all { it.didReceiveResponse }, retrievedResponses.any { it.noTanMethodSelected }, ) : FinTsClientResponse(isSuccessful(retrievedResponses), retrievedResponses.all { it.didReceiveResponse }, retrievedResponses.any { it.noTanMethodSelected },
retrievedResponses.any { it.isStrongAuthenticationRequired }, retrievedResponses.map { it.tanRequired }.firstOrNull(), retrievedResponses.any { it.isStrongAuthenticationRequired }, retrievedResponses.map { it.tanRequired }.firstOrNull(),
retrievedResponses.flatMap { it.messageLogWithoutSensitiveData }, retrievedResponses.flatMap { it.messageLog },
errorMessage ?: retrievedResponses.mapNotNull { it.internalError }.joinToString("\r\n"), errorMessage ?: retrievedResponses.mapNotNull { it.internalError }.joinToString("\r\n"),
retrievedResponses.flatMap { it.errorMessagesFromBank }, retrievedResponses.any { it.isPinLocked }, retrievedResponses.flatMap { it.errorMessagesFromBank }, retrievedResponses.any { it.isPinLocked },
retrievedResponses.any { it.wrongCredentialsEntered }, retrievedResponses.any { it.userCancelledAction }, retrievedResponses.any { it.wrongCredentialsEntered }, retrievedResponses.any { it.userCancelledAction },