Fixed determining supported tan procedures for user
This commit is contained in:
parent
c0403b0be7
commit
62597274fc
|
@ -2,10 +2,10 @@ package net.dankito.fints
|
||||||
|
|
||||||
import net.dankito.fints.messages.MessageBuilder
|
import net.dankito.fints.messages.MessageBuilder
|
||||||
import net.dankito.fints.messages.MessageBuilderResult
|
import net.dankito.fints.messages.MessageBuilderResult
|
||||||
import net.dankito.fints.messages.datenelemente.implementierte.BPDVersion
|
|
||||||
import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache
|
import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache
|
||||||
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID
|
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID
|
||||||
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte
|
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte
|
||||||
|
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
|
||||||
import net.dankito.fints.model.*
|
import net.dankito.fints.model.*
|
||||||
import net.dankito.fints.response.InstituteSegmentId
|
import net.dankito.fints.response.InstituteSegmentId
|
||||||
import net.dankito.fints.response.Response
|
import net.dankito.fints.response.Response
|
||||||
|
@ -86,6 +86,29 @@ open class FinTsClient @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open fun getBankAndCustomerInfoForNewUser(bank: BankData, customer: CustomerData): FinTsClientResponse {
|
||||||
|
val dialogData = DialogData()
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
customer.resetUpdVersion()
|
||||||
|
/**
|
||||||
|
* Sind dem Kundenprodukt die konkreten, für den Benutzer zugelassenen Sicherheitsverfahren nicht bekannt, so können
|
||||||
|
* diese über eine Dialoginitialisierung mit Sicherheitsfunktion=999 angefordert werden. Die konkreten Verfahren
|
||||||
|
* werden dann über den Rückmeldungscode=3920 zurückgemeldet. Im Rahmen dieses Prozesses darf keine UPD
|
||||||
|
* zurückgeliefert werden und die Durchführung anderer Geschäftsvorfälle ist in einem solchen Dialog nicht erlaubt.
|
||||||
|
*/
|
||||||
|
customer.resetSelectedTanProcedure()
|
||||||
|
|
||||||
|
val initDialogResponse = initDialogWithoutChecks(bank, customer, dialogData, false)
|
||||||
|
|
||||||
|
closeDialog(bank, customer, dialogData)
|
||||||
|
|
||||||
|
return FinTsClientResponse(initDialogResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some banks support that according to PSD2 account transactions may be retrieved without
|
* Some banks support that according to PSD2 account transactions may be retrieved without
|
||||||
* a TAN (= no strong customer authorization needed).
|
* a TAN (= no strong customer authorization needed).
|
||||||
|
@ -245,7 +268,7 @@ open class FinTsClient @JvmOverloads constructor(
|
||||||
protected open fun initDialog(bank: BankData, customer: CustomerData, dialogData: DialogData): Response {
|
protected open fun initDialog(bank: BankData, customer: CustomerData, dialogData: DialogData): Response {
|
||||||
|
|
||||||
// we first need to retrieve supported tan procedures and jobs before we can do anything
|
// we first need to retrieve supported tan procedures and jobs before we can do anything
|
||||||
val retrieveBasicBankDataResponse = ensureBasicBankDataRetrieved(bank)
|
val retrieveBasicBankDataResponse = ensureBasicBankDataRetrieved(bank, customer)
|
||||||
if (retrieveBasicBankDataResponse.successful == false) {
|
if (retrieveBasicBankDataResponse.successful == false) {
|
||||||
return retrieveBasicBankDataResponse
|
return retrieveBasicBankDataResponse
|
||||||
}
|
}
|
||||||
|
@ -257,8 +280,13 @@ open class FinTsClient @JvmOverloads constructor(
|
||||||
return tanProcedureSelectedResponse
|
return tanProcedureSelectedResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return initDialogWithoutChecks(bank, customer, dialogData, true)
|
||||||
|
}
|
||||||
|
|
||||||
val requestBody = messageBuilder.createInitDialogMessage(bank, customer, product, dialogData)
|
protected open fun initDialogWithoutChecks(bank: BankData, customer: CustomerData, dialogData: DialogData,
|
||||||
|
useStrongAuthentication: Boolean = true): Response {
|
||||||
|
|
||||||
|
val requestBody = messageBuilder.createInitDialogMessage(bank, customer, product, dialogData, useStrongAuthentication)
|
||||||
|
|
||||||
val response = getAndHandleResponseForMessage(requestBody, bank)
|
val response = getAndHandleResponseForMessage(requestBody, bank)
|
||||||
|
|
||||||
|
@ -322,11 +350,11 @@ open class FinTsClient @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected open fun ensureBasicBankDataRetrieved(bank: BankData): Response {
|
protected open fun ensureBasicBankDataRetrieved(bank: BankData, customer: CustomerData): Response {
|
||||||
if (bank.supportedTanProcedures.isEmpty() || bank.supportedJobs.isEmpty()) {
|
if (bank.supportedTanProcedures.isEmpty() || bank.supportedJobs.isEmpty()) {
|
||||||
bank.bpdVersion = BPDVersion.VersionNotReceivedYet
|
bank.resetBpdVersion()
|
||||||
|
|
||||||
val getBankInfoResponse = getAnonymousBankInfo(bank)
|
val getBankInfoResponse = getBankAndCustomerInfoForNewUser(bank, customer)
|
||||||
|
|
||||||
if (getBankInfoResponse.isSuccessful == false || bank.supportedTanProcedures.isEmpty()
|
if (getBankInfoResponse.isSuccessful == false || bank.supportedTanProcedures.isEmpty()
|
||||||
|| bank.supportedJobs.isEmpty()) {
|
|| bank.supportedJobs.isEmpty()) {
|
||||||
|
@ -339,37 +367,23 @@ open class FinTsClient @JvmOverloads constructor(
|
||||||
return Response(true)
|
return Response(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: glatt ziehen
|
|
||||||
protected open fun ensureTanProcedureIsSelected(bank: BankData, customer: CustomerData): Response {
|
protected open fun ensureTanProcedureIsSelected(bank: BankData, customer: CustomerData): Response {
|
||||||
var askWithProceduresSupportedByBank = false
|
if (customer.isTanProcedureSelected == false) {
|
||||||
|
if (customer.supportedTanProcedures.isEmpty()) {
|
||||||
|
getBankAndCustomerInfoForNewUser(bank, customer)
|
||||||
|
}
|
||||||
|
|
||||||
if (bank.supportedTanProcedures.isEmpty() && customer.selectedTanProcedure == null) { // no tan procedures ever received
|
if (customer.supportedTanProcedures.isEmpty()) { // could not retrieve supported tan procedures for user
|
||||||
getAnonymousBankInfo(bank)
|
return Response(false, noTanProcedureSelected = true)
|
||||||
|
}
|
||||||
|
|
||||||
if (bank.supportedTanProcedures.isNotEmpty()) { // TODO: what if we didn't receive any? find a workaround for this
|
// we know user's supported tan procedure, now ask user which one to select
|
||||||
|
callback.askUserForTanProcedure(customer.supportedTanProcedures)?.let {
|
||||||
customer.selectedTanProcedure =
|
customer.selectedTanProcedure = it
|
||||||
callback.askUserForTanProcedure(bank.supportedTanProcedures) // a bit problematic as user is not necessarily allowed to use all procedures bank supports
|
|
||||||
|
|
||||||
askWithProceduresSupportedByBank = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customer.selectedTanProcedure == null) {
|
return Response(customer.isTanProcedureSelected, noTanProcedureSelected = !!!customer.isTanProcedureSelected)
|
||||||
if (customer.supportedTanProcedures.isNotEmpty()) {
|
|
||||||
customer.selectedTanProcedure =
|
|
||||||
callback.askUserForTanProcedure(customer.supportedTanProcedures)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (askWithProceduresSupportedByBank == false && bank.supportedTanProcedures.isNotEmpty()) {
|
|
||||||
customer.selectedTanProcedure =
|
|
||||||
callback.askUserForTanProcedure(bank.supportedTanProcedures)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val noTanProcedureSelected = customer.selectedTanProcedure == null
|
|
||||||
|
|
||||||
return Response(!!!noTanProcedureSelected, noTanProcedureSelected = noTanProcedureSelected)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -511,20 +525,16 @@ open class FinTsClient @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response.getFirstSegmentById<TanInfo>(InstituteSegmentId.TanInfo)?.let { tanInfo ->
|
if (response.supportedTanProceduresForUser.isNotEmpty()) {
|
||||||
customer.supportedTanProcedures = mapToTanProcedures(tanInfo)
|
customer.supportedTanProcedures = response.supportedTanProceduresForUser.mapNotNull { findTanProcedure(it, bank) }
|
||||||
|
|
||||||
customer.selectedTanProcedure?.let { selectedProcedure ->
|
|
||||||
if (customer.supportedTanProcedures.isNotEmpty() &&
|
|
||||||
customer.supportedTanProcedures.contains(selectedProcedure) == false) {
|
|
||||||
// currently selected tan procedure is not in list of supported procedures -> ask user the next time when needed
|
|
||||||
customer.selectedTanProcedure = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setAllowedJobsForAccount(account: AccountData, supportedJobs: List<SupportedJob>) {
|
protected open fun findTanProcedure(securityFunction: Sicherheitsfunktion, bank: BankData): TanProcedure? {
|
||||||
|
return bank.supportedTanProcedures.firstOrNull { it.securityFunction == securityFunction }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun setAllowedJobsForAccount(account: AccountData, supportedJobs: List<SupportedJob>) {
|
||||||
val allowedJobsForAccount = mutableListOf<SupportedJob>()
|
val allowedJobsForAccount = mutableListOf<SupportedJob>()
|
||||||
|
|
||||||
for (job in supportedJobs) {
|
for (job in supportedJobs) {
|
||||||
|
|
|
@ -61,13 +61,19 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open fun createInitDialogMessage(bank: BankData, customer: CustomerData, product: ProductData, dialogData: DialogData): String {
|
open fun createInitDialogMessage(bank: BankData, customer: CustomerData, product: ProductData,
|
||||||
|
dialogData: DialogData, useStrongAuthentication: Boolean = true): String {
|
||||||
|
|
||||||
return createSignedMessage(bank, customer, dialogData, listOf(
|
val segments = mutableListOf(
|
||||||
IdentifikationsSegment(generator.resetSegmentNumber(2), bank, customer),
|
IdentifikationsSegment(generator.resetSegmentNumber(2), bank, customer),
|
||||||
Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), bank, customer, product),
|
Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), bank, customer, product)
|
||||||
ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.Identification)
|
)
|
||||||
))
|
|
||||||
|
if (useStrongAuthentication) {
|
||||||
|
segments.add(ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.Identification))
|
||||||
|
}
|
||||||
|
|
||||||
|
return createSignedMessage(bank, customer, dialogData, segments)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun createSynchronizeCustomerSystemIdMessage(bank: BankData, customer: CustomerData, product: ProductData, dialogData: DialogData): String {
|
open fun createSynchronizeCustomerSystemIdMessage(bank: BankData, customer: CustomerData, product: ProductData, dialogData: DialogData): String {
|
||||||
|
|
|
@ -37,7 +37,7 @@ open class Signaturkopf(
|
||||||
) : Segment(listOf(
|
) : Segment(listOf(
|
||||||
Segmentkopf(MessageSegmentId.SignatureHeader, 4, segmentNumber), // allowed
|
Segmentkopf(MessageSegmentId.SignatureHeader, 4, segmentNumber), // allowed
|
||||||
Sicherheitsprofil(Sicherheitsverfahren.PIN_TAN_Verfahren, VersionDesSicherheitsverfahrens.PIN_Zwei_Schritt), // fints4java only supports Pin/Tan and PSD2 requires two step tan procedure
|
Sicherheitsprofil(Sicherheitsverfahren.PIN_TAN_Verfahren, VersionDesSicherheitsverfahrens.PIN_Zwei_Schritt), // fints4java only supports Pin/Tan and PSD2 requires two step tan procedure
|
||||||
SicherheitsfunktionKodiert(customer.selectedTanProcedure?.securityFunction!!), // allowed: 1, 2
|
SicherheitsfunktionKodiert(customer.selectedTanProcedure.securityFunction),
|
||||||
Sicherheitskontrollreferenz(securityControlReference), // allowed: <>0
|
Sicherheitskontrollreferenz(securityControlReference), // allowed: <>0
|
||||||
BereichDerSicherheitsapplikationKodiert(BereichDerSicherheitsapplikation.SignaturkopfUndHBCINutzdaten), // allowed: 1 ?
|
BereichDerSicherheitsapplikationKodiert(BereichDerSicherheitsapplikation.SignaturkopfUndHBCINutzdaten), // allowed: 1 ?
|
||||||
RolleDesSicherheitslieferantenKodiert(), // allowed: 1
|
RolleDesSicherheitslieferantenKodiert(), // allowed: 1
|
||||||
|
|
|
@ -46,7 +46,7 @@ open class Verschluesselungskopf(
|
||||||
) : Segment(listOf(
|
) : Segment(listOf(
|
||||||
Segmentkopf(MessageSegmentId.EncryptionHeader, 3, 998),
|
Segmentkopf(MessageSegmentId.EncryptionHeader, 3, 998),
|
||||||
Sicherheitsprofil(Sicherheitsverfahren.PIN_TAN_Verfahren, VersionDesSicherheitsverfahrens.PIN_Zwei_Schritt), // fints4java only supports Pin/Tan and PSD2 requires two step tan procedure
|
Sicherheitsprofil(Sicherheitsverfahren.PIN_TAN_Verfahren, VersionDesSicherheitsverfahrens.PIN_Zwei_Schritt), // fints4java only supports Pin/Tan and PSD2 requires two step tan procedure
|
||||||
SicherheitsfunktionKodiert(Sicherheitsfunktion.Klartext), // allowed: 4
|
SicherheitsfunktionKodiert(Sicherheitsfunktion.Klartext),
|
||||||
RolleDesSicherheitslieferantenKodiert(), // allowed: 1, 4
|
RolleDesSicherheitslieferantenKodiert(), // allowed: 1, 4
|
||||||
SicherheitsidentifikationDetails(customer.customerSystemId),
|
SicherheitsidentifikationDetails(customer.customerSystemId),
|
||||||
SicherheitsdatumUndUhrzeit(date, time),
|
SicherheitsdatumUndUhrzeit(date, time),
|
||||||
|
|
|
@ -27,6 +27,11 @@ open class BankData(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
open fun resetBpdVersion() {
|
||||||
|
bpdVersion = BPDVersion.VersionNotReceivedYet
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "$name ($bankCode)"
|
return "$name ($bankCode)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
package net.dankito.fints.model
|
package net.dankito.fints.model
|
||||||
|
|
||||||
import net.dankito.fints.messages.datenelemente.implementierte.*
|
import net.dankito.fints.messages.datenelemente.implementierte.*
|
||||||
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsverfahren
|
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
|
||||||
import net.dankito.fints.messages.datenelemente.implementierte.signatur.VersionDesSicherheitsverfahrens
|
|
||||||
|
|
||||||
|
|
||||||
open class CustomerData(
|
open class CustomerData(
|
||||||
|
@ -14,7 +13,7 @@ open class CustomerData(
|
||||||
var accounts: List<AccountData> = listOf(),
|
var accounts: List<AccountData> = listOf(),
|
||||||
var updVersion: Int = UPDVersion.VersionNotReceivedYet,
|
var updVersion: Int = UPDVersion.VersionNotReceivedYet,
|
||||||
var supportedTanProcedures: List<TanProcedure> = listOf(),
|
var supportedTanProcedures: List<TanProcedure> = listOf(),
|
||||||
var selectedTanProcedure: TanProcedure? = null,
|
var selectedTanProcedure: TanProcedure = TanProcedureNotSelected,
|
||||||
var selectedLanguage: Dialogsprache = Dialogsprache.Default,
|
var selectedLanguage: Dialogsprache = Dialogsprache.Default,
|
||||||
var customerSystemId: String = KundensystemID.Anonymous,
|
var customerSystemId: String = KundensystemID.Anonymous,
|
||||||
var customerSystemStatus: KundensystemStatusWerte = KundensystemStatus.SynchronizingCustomerSystemId,
|
var customerSystemStatus: KundensystemStatusWerte = KundensystemStatus.SynchronizingCustomerSystemId,
|
||||||
|
@ -23,6 +22,10 @@ open class CustomerData(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
val SecurityFunctionNotSelected = Sicherheitsfunktion.Einschritt_Verfahren
|
||||||
|
|
||||||
|
val TanProcedureNotSelected = TanProcedure("NOT_SELECTED", SecurityFunctionNotSelected, TanProcedureType.EnterTan)
|
||||||
|
|
||||||
val Anonymous = CustomerData(KundenID.Anonymous, "", customerSystemStatus = KundensystemStatusWerte.NichtBenoetigt)
|
val Anonymous = CustomerData(KundenID.Anonymous, "", customerSystemStatus = KundensystemStatusWerte.NichtBenoetigt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +34,19 @@ open class CustomerData(
|
||||||
constructor(customerId: String, pin: String) : this(customerId, pin, customerId)
|
constructor(customerId: String, pin: String) : this(customerId, pin, customerId)
|
||||||
|
|
||||||
|
|
||||||
|
val isTanProcedureSelected: Boolean
|
||||||
|
get() = selectedTanProcedure != TanProcedureNotSelected
|
||||||
|
|
||||||
|
|
||||||
|
open fun resetSelectedTanProcedure() {
|
||||||
|
selectedTanProcedure = TanProcedureNotSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun resetUpdVersion() {
|
||||||
|
updVersion = UPDVersion.VersionNotReceivedYet
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return customerId
|
return customerId
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,11 @@ class FinTsClientTest {
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(result.isSuccessful).isTrue()
|
assertThat(result.isSuccessful).isTrue()
|
||||||
|
assertThat(BankDataAnonymous.supportedHbciVersions).isNotEmpty()
|
||||||
|
assertThat(BankDataAnonymous.supportedTanProcedures).isNotEmpty()
|
||||||
|
assertThat(BankDataAnonymous.supportedJobs).isNotEmpty()
|
||||||
|
assertThat(BankDataAnonymous.supportedLanguages).isNotEmpty()
|
||||||
|
assertThat(BankDataAnonymous.name).isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue