Implemented parsing HITANS version 7
This commit is contained in:
parent
87662d776a
commit
e08076de17
|
@ -1141,13 +1141,13 @@ open class FinTsClient(
|
|||
return when {
|
||||
// names are like 'chipTAN (comfort) manuell', 'Smart(-)TAN plus (manuell)' and
|
||||
// technical identification is 'HHD'. Exception: there's one that states itself as 'chipTAN (Manuell)'
|
||||
// but its ZkaTanMethod is set to 'HHDOPT1' -> handle ChipTanManuell before ChipTanFlickercode
|
||||
parameters.zkaTanMethod == ZkaTanMethod.HHD || name.contains("manuell") ->
|
||||
// but its DkTanMethod is set to 'HHDOPT1' -> handle ChipTanManuell before ChipTanFlickercode
|
||||
parameters.dkTanMethod == DkTanMethod.HHD || name.contains("manuell") ->
|
||||
TanMethodType.ChipTanManuell
|
||||
|
||||
// names are like 'chipTAN optisch/comfort', 'SmartTAN (plus) optic/USB', 'chipTAN (Flicker)' and
|
||||
// technical identification is 'HHDOPT1'
|
||||
parameters.zkaTanMethod == ZkaTanMethod.HHDOPT1 ||
|
||||
parameters.dkTanMethod == DkTanMethod.HHDOPT1 ||
|
||||
tanMethodNameContains(name, "optisch", "optic", "comfort", "flicker") ->
|
||||
TanMethodType.ChipTanFlickercode
|
||||
|
||||
|
@ -1172,7 +1172,8 @@ open class FinTsClient(
|
|||
|
||||
// 'flateXSecure' identifies itself as 'PPTAN' instead of 'AppTAN'
|
||||
// 'activeTAN-Verfahren' can actually be used either with an app or a reader; it's like chipTAN QR but without a chip card
|
||||
tanMethodNameContains(name, "push", "app", "BestSign", "SecureGo", "TAN2go", "activeTAN", "easyTAN", "SecurePlus", "TAN+")
|
||||
parameters.dkTanMethod == DkTanMethod.App
|
||||
|| tanMethodNameContains(name, "push", "app", "BestSign", "SecureGo", "TAN2go", "activeTAN", "easyTAN", "SecurePlus", "TAN+")
|
||||
|| technicalTanMethodIdentificationContains(parameters, "SECURESIGN", "PPTAN") ->
|
||||
TanMethodType.AppTan
|
||||
|
||||
|
@ -1185,8 +1186,8 @@ open class FinTsClient(
|
|||
return when {
|
||||
technicalTanMethodIdentificationContains(parameters, "HHD1.4") -> HHDVersion.HHD_1_4
|
||||
technicalTanMethodIdentificationContains(parameters, "HHD1.3") -> HHDVersion.HHD_1_3
|
||||
parameters.versionZkaTanMethod?.contains("1.4") == true -> HHDVersion.HHD_1_4
|
||||
parameters.versionZkaTanMethod?.contains("1.3") == true -> HHDVersion.HHD_1_4
|
||||
parameters.versionDkTanMethod?.contains("1.4") == true -> HHDVersion.HHD_1_4
|
||||
parameters.versionDkTanMethod?.contains("1.3") == true -> HHDVersion.HHD_1_4
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package net.dankito.banking.fints.messages.datenelemente.implementierte.tan
|
||||
|
||||
|
||||
enum class ZkaTanMethod {
|
||||
enum class DkTanMethod {
|
||||
|
||||
HHD,
|
||||
|
||||
|
@ -11,8 +11,10 @@ enum class ZkaTanMethod {
|
|||
|
||||
mobileTAN,
|
||||
|
||||
// values not specified by standard but found out there in the wild
|
||||
App,
|
||||
|
||||
appTAN
|
||||
Decoupled,
|
||||
|
||||
DecoupledPush
|
||||
|
||||
}
|
|
@ -10,7 +10,7 @@ open class TanMethod(
|
|||
open val type: TanMethodType,
|
||||
open val hhdVersion: HHDVersion? = null,
|
||||
open val maxTanInputLength: Int? = null,
|
||||
open val allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric,
|
||||
open val allowedTanFormat: AllowedTanFormat? = null,
|
||||
open val nameOfTanMediumRequired: Boolean = false
|
||||
) {
|
||||
|
||||
|
|
|
@ -390,33 +390,57 @@ open class ResponseParser(
|
|||
var remainingDataElements = methodsDataElements
|
||||
|
||||
while (remainingDataElements.size >= 20) { // parameters have at least 20 data elements, the last element is optional
|
||||
val dataElementForNextMethod = if (remainingDataElements.size >= 21) remainingDataElements.subList(0, 21)
|
||||
else remainingDataElements.subList(0, 20)
|
||||
|
||||
val methodParameters = mapToSingleTanMethodParameters(dataElementForNextMethod)
|
||||
val methodParameters = mapToSingleTanMethodParameters(remainingDataElements)
|
||||
parsedMethodsParameters.add(methodParameters)
|
||||
|
||||
val has21ElementsParsed = methodParameters.countSupportedActiveTanMedia != null ||
|
||||
(dataElementForNextMethod.size >= 21 && dataElementForNextMethod[20].isBlank())
|
||||
|
||||
if (has21ElementsParsed) remainingDataElements = remainingDataElements.subList(21, remainingDataElements.size)
|
||||
else remainingDataElements = remainingDataElements.subList(20, remainingDataElements.size)
|
||||
val countParsedDataElements = determineCountParsedTanMethodParametersDataElements(remainingDataElements, methodParameters)
|
||||
remainingDataElements = remainingDataElements.subList(countParsedDataElements, remainingDataElements.size)
|
||||
}
|
||||
|
||||
return parsedMethodsParameters
|
||||
}
|
||||
|
||||
protected open fun determineCountParsedTanMethodParametersDataElements(remainingDataElements: List<String>, parsedMethodParameters: TanMethodParameters): Int {
|
||||
// last TanMethodParameters data elements
|
||||
if (remainingDataElements.size == 20) {
|
||||
return 20
|
||||
}
|
||||
else if (remainingDataElements.size == 21) {
|
||||
return 21
|
||||
}
|
||||
|
||||
if (parsedMethodParameters.dkTanMethod == DkTanMethod.Decoupled) {
|
||||
if (parsedMethodParameters.periodicStateRequestsAllowedForDecoupled != null) {
|
||||
return 26
|
||||
}
|
||||
else if (parsedMethodParameters.manualConfirmationAllowedForDecoupled != null) {
|
||||
return 26
|
||||
}
|
||||
else {
|
||||
return 24
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedMethodParameters.countSupportedActiveTanMedia != null || remainingDataElements[20].isBlank()) {
|
||||
return 21
|
||||
}
|
||||
|
||||
return 20
|
||||
}
|
||||
|
||||
protected open fun mapToSingleTanMethodParameters(methodDataElements: List<String>): TanMethodParameters {
|
||||
val dkTanMethod = tryToParseDkTanMethod(methodDataElements[3])
|
||||
val isDecoupledTanMethod = dkTanMethod == DkTanMethod.Decoupled || dkTanMethod == DkTanMethod.DecoupledPush
|
||||
|
||||
return TanMethodParameters(
|
||||
parseCodeEnum(methodDataElements[0], Sicherheitsfunktion.values()),
|
||||
parseCodeEnum(methodDataElements[1], TanProcess.values()),
|
||||
parseString(methodDataElements[2]),
|
||||
tryToParseZkaTanMethod(methodDataElements[3]),
|
||||
dkTanMethod,
|
||||
parseStringToNullIfEmpty(methodDataElements[4]),
|
||||
parseString(methodDataElements[5]),
|
||||
parseInt(methodDataElements[6]),
|
||||
parseCodeEnum(methodDataElements[7], AllowedTanFormat.values()),
|
||||
if (isDecoupledTanMethod) null else parseNullableInt(methodDataElements[6]),
|
||||
if (isDecoupledTanMethod) null else parseCodeEnum(methodDataElements[7], AllowedTanFormat.values()),
|
||||
parseString(methodDataElements[8]),
|
||||
parseInt(methodDataElements[9]),
|
||||
// for HITANS 4 and 5 here is another "Anzahl unterstützter aktiver TAN-Listen" Integer element
|
||||
|
@ -431,30 +455,35 @@ open class ResponseParser(
|
|||
parseCodeEnum(methodDataElements[17], Initialisierungsmodus.values()),
|
||||
parseCodeEnum(methodDataElements[18], BezeichnungDesTanMediumsErforderlich.values()),
|
||||
parseBoolean(methodDataElements[19]),
|
||||
if (methodDataElements.size > 20) parseNullableInt(methodDataElements[20]) else null
|
||||
if (methodDataElements.size > 20) parseNullableInt(methodDataElements[20]) else null,
|
||||
if (isDecoupledTanMethod && methodDataElements.size > 21) parseNullableInt(methodDataElements[21]) else null,
|
||||
if (isDecoupledTanMethod && methodDataElements.size > 22) parseNullableInt(methodDataElements[22]) else null,
|
||||
if (isDecoupledTanMethod && methodDataElements.size > 23) parseNullableInt(methodDataElements[23]) else null,
|
||||
if (isDecoupledTanMethod && methodDataElements.size > 24) parseNullableBoolean(methodDataElements[24]) else null,
|
||||
if (isDecoupledTanMethod && methodDataElements.size > 25) parseNullableBoolean(methodDataElements[25]) else null
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun tryToParseZkaTanMethod(mayZkaTanMethod: String): ZkaTanMethod? {
|
||||
if (mayZkaTanMethod.isBlank()) {
|
||||
protected open fun tryToParseDkTanMethod(mayDkTanMethod: String): DkTanMethod? {
|
||||
if (mayDkTanMethod.isBlank()) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
val lowerCaseMayZkaTanMethod = mayZkaTanMethod.toLowerCase()
|
||||
val lowerCaseMayDkTanMethod = mayDkTanMethod.toLowerCase()
|
||||
|
||||
if (lowerCaseMayZkaTanMethod == "mobiletan" || lowerCaseMayZkaTanMethod == "mtan") {
|
||||
return ZkaTanMethod.mobileTAN
|
||||
if (lowerCaseMayDkTanMethod == "mobiletan" || lowerCaseMayDkTanMethod == "mtan") {
|
||||
return DkTanMethod.mobileTAN
|
||||
}
|
||||
|
||||
if (lowerCaseMayZkaTanMethod == "apptan" || lowerCaseMayZkaTanMethod == "phototan") {
|
||||
return ZkaTanMethod.appTAN
|
||||
if (lowerCaseMayDkTanMethod == "apptan" || lowerCaseMayDkTanMethod == "phototan") {
|
||||
return DkTanMethod.App
|
||||
}
|
||||
|
||||
// TODO: what about these values, all returned by banks in anonymous dialog initialization:
|
||||
// BestSign, HHDUSB1, Secoder_UC, ZkaTANMode, photoTAN, QRTAN, 1822TAN+
|
||||
|
||||
return ZkaTanMethod.valueOf(mayZkaTanMethod)
|
||||
return DkTanMethod.valueOf(mayDkTanMethod)
|
||||
} catch (ignored: Exception) { }
|
||||
|
||||
return null
|
||||
|
@ -892,6 +921,14 @@ open class ResponseParser(
|
|||
.replace(Separators.MaskingCharacter + Separators.MaskingCharacter, Separators.MaskingCharacter)
|
||||
}
|
||||
|
||||
protected open fun parseNullableBoolean(mayBoolean: String): Boolean? {
|
||||
try {
|
||||
return parseBoolean(mayBoolean)
|
||||
} catch (ignored: Exception) { }
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
protected open fun parseBoolean(dataElement: String): Boolean {
|
||||
if ("J" == dataElement) {
|
||||
return true
|
||||
|
|
|
@ -8,11 +8,11 @@ open class TanMethodParameters(
|
|||
val securityFunction: Sicherheitsfunktion,
|
||||
val tanProcess: TanProcess,
|
||||
val technicalTanMethodIdentification: String,
|
||||
val zkaTanMethod: ZkaTanMethod?,
|
||||
val versionZkaTanMethod: String?,
|
||||
val dkTanMethod: DkTanMethod?,
|
||||
val versionDkTanMethod: String?,
|
||||
val methodName: String,
|
||||
val maxTanInputLength: Int,
|
||||
val allowedTanFormat: AllowedTanFormat,
|
||||
val maxTanInputLength: Int?,
|
||||
val allowedTanFormat: AllowedTanFormat?,
|
||||
val descriptionToShowToUser: String,
|
||||
val maxReturnValueLength: Int,
|
||||
val multipleTansAllowed: Boolean,
|
||||
|
@ -25,7 +25,12 @@ open class TanMethodParameters(
|
|||
val initializingMode: Initialisierungsmodus,
|
||||
val nameOfTanMediumRequired: BezeichnungDesTanMediumsErforderlich,
|
||||
val hhdUcResponseRequired: Boolean, // TODO: wird hierueber gesteuert ob eine TAN eingegeben werden muss (z. B. beim EasyTAN Verfahren muss ja keine eingegeben werden)
|
||||
val countSupportedActiveTanMedia: Int?
|
||||
val countSupportedActiveTanMedia: Int?,
|
||||
val maxNumberOfStateRequestsForDecoupled: Int? = null,
|
||||
val initialDelayInSecondsForStateRequestsForDecoupled: Int? = null,
|
||||
val delayInSecondsForNextStateRequestsForDecoupled: Int? = null,
|
||||
val manualConfirmationAllowedForDecoupled: Boolean? = null,
|
||||
val periodicStateRequestsAllowedForDecoupled: Boolean? = null
|
||||
) {
|
||||
|
||||
|
||||
|
|
|
@ -710,7 +710,7 @@ class ResponseParserTest : FinTsTestBase() {
|
|||
|
||||
|
||||
@Test
|
||||
fun parseTanInfo() {
|
||||
fun parseTanInfo_1() {
|
||||
|
||||
// when
|
||||
val result = underTest.parse("HITANS:171:6:4+1+1+1+J:N:0:910:2:HHD1.3.0:::chipTAN manuell:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:911:2:HHD1.3.2OPT:HHDOPT1:1.3.2:chipTAN optisch:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:912:2:HHD1.3.2USB:HHDUSB1:1.3.2:chipTAN-USB:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:913:2:Q1S:Secoder_UC:1.2.0:chipTAN-QR:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:920:2:smsTAN:::smsTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:2:N:5:921:2:pushTAN:::pushTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:2:N:2:900:2:iTAN:::iTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:0'")
|
||||
|
@ -734,6 +734,84 @@ class ResponseParserTest : FinTsTestBase() {
|
|||
?: run { fail("No segment of type TanInfo found in ${result.receivedSegments}") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseTanInfo6_2() {
|
||||
|
||||
// when
|
||||
val result = underTest.parse("HITANS:77:6:3+1+1+1+J:N:0:942:2:MTAN2:mobileTAN::mobile TAN:6:1:SMS:2048:J:1:N:0:2:N:J:00:0:N:1:944:2:SECUREGO:mobileTAN::SecureGo:6:1:TAN:2048:J:1:N:0:2:N:J:00:0:N:1:962:2:HHD1.4:HHD:1.4:Smart-TAN plus manuell:6:1:Challenge:2048:J:1:N:0:2:N:J:00:0:N:1:972:2:HHD1.4OPT:HHDOPT1:1.4:Smart-TAN plus optisch / USB:6:1:Challenge:2048:J:1:N:0:2:N:J:00:0:N:1:982:2:MS1.0.0:::Smart-TAN photo:6:1:Challenge:2048:J:1:N:0:2:N:J:00:0:N:1'")
|
||||
|
||||
// then
|
||||
assertSuccessfullyParsedSegment(result, InstituteSegmentId.TanInfo, 77, 6, 3)
|
||||
|
||||
result.getFirstSegmentById<TanInfo>(InstituteSegmentId.TanInfo)?.let { segment ->
|
||||
val tanMethodParameters = segment.tanProcedureParameters.methodParameters
|
||||
expect(tanMethodParameters).hasSize(5)
|
||||
|
||||
assertTanMethodParameter(tanMethodParameters, 0, Sicherheitsfunktion.PIN_TAN_942, DkTanMethod.mobileTAN, "mobile TAN")
|
||||
assertTanMethodParameter(tanMethodParameters, 1, Sicherheitsfunktion.PIN_TAN_944, DkTanMethod.mobileTAN, "SecureGo")
|
||||
assertTanMethodParameter(tanMethodParameters, 2, Sicherheitsfunktion.PIN_TAN_962, DkTanMethod.HHD, "Smart-TAN plus manuell")
|
||||
assertTanMethodParameter(tanMethodParameters, 3, Sicherheitsfunktion.PIN_TAN_972, DkTanMethod.HHDOPT1, "Smart-TAN plus optisch / USB")
|
||||
assertTanMethodParameter(tanMethodParameters, 4, Sicherheitsfunktion.PIN_TAN_982, null, "Smart-TAN photo")
|
||||
}
|
||||
?: run { fail("No segment of type TanInfo found in ${result.receivedSegments}") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseTanInfo6_3() {
|
||||
|
||||
// when
|
||||
val result = underTest.parse("HITANS:169:6:3+1+1+1+J:N:0:910:2:HHD1.3.0:::chipTAN manuell:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:911:2:HHD1.3.2OPT:HHDOPT1:1.3.2:chipTAN optisch:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:912:2:HHD1.3.2USB:HHDUSB1:1.3.2:chipTAN-USB:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:913:2:Q1S:Secoder_UC:1.2.0:chipTAN-QR:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:1:920:2:smsTAN:::smsTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:2:N:5:921:2:pushTAN:::pushTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:2:N:2:900:2:iTAN:::iTAN:6:1:TAN-Nummer:3:J:2:N:0:0:N:N:00:0:N:0'")
|
||||
|
||||
// then
|
||||
assertSuccessfullyParsedSegment(result, InstituteSegmentId.TanInfo, 169, 6, 3)
|
||||
|
||||
result.getFirstSegmentById<TanInfo>(InstituteSegmentId.TanInfo)?.let { segment ->
|
||||
val tanMethodParameters = segment.tanProcedureParameters.methodParameters
|
||||
expect(tanMethodParameters).hasSize(7)
|
||||
|
||||
assertTanMethodParameter(tanMethodParameters, 0, Sicherheitsfunktion.PIN_TAN_910, null, "chipTAN manuell")
|
||||
assertTanMethodParameter(tanMethodParameters, 1, Sicherheitsfunktion.PIN_TAN_911, DkTanMethod.HHDOPT1, "chipTAN optisch")
|
||||
assertTanMethodParameter(tanMethodParameters, 2, Sicherheitsfunktion.PIN_TAN_912, null, "chipTAN-USB") // TODO: parse not specified 'HHDUSB1' to DkTanMethod?
|
||||
assertTanMethodParameter(tanMethodParameters, 3, Sicherheitsfunktion.PIN_TAN_913, null, "chipTAN-QR") // TODO: parse not specified 'Secoder_UC' to DkTanMethod?
|
||||
assertTanMethodParameter(tanMethodParameters, 4, Sicherheitsfunktion.PIN_TAN_920, null, "smsTAN")
|
||||
assertTanMethodParameter(tanMethodParameters, 5, Sicherheitsfunktion.PIN_TAN_921, null, "pushTAN")
|
||||
assertTanMethodParameter(tanMethodParameters, 6, Sicherheitsfunktion.PIN_TAN_900, null, "iTAN")
|
||||
}
|
||||
?: run { fail("No segment of type TanInfo found in ${result.receivedSegments}") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseTanInfo6_4() {
|
||||
|
||||
// when
|
||||
val result = underTest.parse("HITANS:54:6:3+1+1+1+N:N:0:901:2:CR#1:::SMS-TAN:6:1:SMS-TAN:256:J:2:J:0:0:N:N:01:0:N:1:904:2:CR#5 - 1.4:HHDOPT1:1.4:chipTAN comfort:6:1:chipTAN comfort:2048:N:1:N:0:0:N:N:01:0:N:1:905:2:CR#6 - 1.4:HHD:1.4:chipTAN comfort manuell:6:1:chipTAN comfort manuell:2048:N:1:N:0:0:N:N:01:0:N:0:906:2:CR#8 - MS1.0:::BV AppTAN:6:1:BV AppTAN:2048:N:1:N:0:0:N:N:01:0:N:0:907:2:MS1.0.0:::PhotoTAN:7:1:PhotoTAN:2048:N:1:N:0:0:N:J:01:0:N:0'")
|
||||
|
||||
// then
|
||||
assertSuccessfullyParsedSegment(result, InstituteSegmentId.TanInfo, 54, 6, 3)
|
||||
|
||||
result.getFirstSegmentById<TanInfo>(InstituteSegmentId.TanInfo)?.let { segment ->
|
||||
val tanMethodParameters = segment.tanProcedureParameters.methodParameters
|
||||
expect(tanMethodParameters).hasSize(5)
|
||||
|
||||
assertTanMethodParameter(tanMethodParameters, 0, Sicherheitsfunktion.PIN_TAN_901, null, "SMS-TAN")
|
||||
assertTanMethodParameter(tanMethodParameters, 1, Sicherheitsfunktion.PIN_TAN_904, DkTanMethod.HHDOPT1, "chipTAN comfort")
|
||||
assertTanMethodParameter(tanMethodParameters, 2, Sicherheitsfunktion.PIN_TAN_905, DkTanMethod.HHD, "chipTAN comfort manuell")
|
||||
assertTanMethodParameter(tanMethodParameters, 3, Sicherheitsfunktion.PIN_TAN_906, null, "BV AppTAN")
|
||||
assertTanMethodParameter(tanMethodParameters, 4, Sicherheitsfunktion.PIN_TAN_907, null, "PhotoTAN")
|
||||
}
|
||||
?: run { fail("No segment of type TanInfo found in ${result.receivedSegments}") }
|
||||
}
|
||||
|
||||
private fun assertTanMethodParameter(parsedTanMethodParameters: List<TanMethodParameters>, index: Int, securityFunction: Sicherheitsfunktion,
|
||||
tanMethod: DkTanMethod?, methodName: String) {
|
||||
|
||||
val tanMethodParameters = parsedTanMethodParameters[index]
|
||||
|
||||
expect(tanMethodParameters.securityFunction).toBe(securityFunction)
|
||||
expect(tanMethodParameters.dkTanMethod).toBe(tanMethod)
|
||||
expect(tanMethodParameters.methodName).toBe(methodName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseTanInfo_CountSupportedActiveTanMediaIsBlank() {
|
||||
|
||||
|
@ -807,6 +885,54 @@ class ResponseParserTest : FinTsTestBase() {
|
|||
?: run { fail("No segment of type TanInfo found in ${result.receivedSegments}") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseTanInfo7_DecoupledpushTan() {
|
||||
|
||||
// when
|
||||
val result = underTest.parse("HITANS:177:7:4+1+1+1+N:N:0:922:2:pushTAN-dec:Decoupled::pushTAN 2.0:::Aufforderung:2048:J:2:N:0:0:N:N:00:2:N:2:180:1:1:J:J'")
|
||||
|
||||
// then
|
||||
assertSuccessfullyParsedSegment(result, InstituteSegmentId.TanInfo, 177, 7, 4)
|
||||
|
||||
result.getFirstSegmentById<TanInfo>(InstituteSegmentId.TanInfo)?.let { segment ->
|
||||
expect(segment.maxCountJobs).toBe(1)
|
||||
expect(segment.minimumCountSignatures).toBe(1)
|
||||
expect(segment.securityClass).toBe(1)
|
||||
expect(segment.tanProcedureParameters.oneStepProcedureAllowed).isFalse()
|
||||
expect(segment.tanProcedureParameters.moreThanOneTanDependentJobPerMessageAllowed).isFalse()
|
||||
expect(segment.tanProcedureParameters.jobHashValue).toBe("0")
|
||||
|
||||
expect(segment.tanProcedureParameters.methodParameters).hasSize(1)
|
||||
|
||||
val decoupledPushTanMethod = segment.tanProcedureParameters.methodParameters.first()
|
||||
|
||||
expect(decoupledPushTanMethod.securityFunction).toBe(Sicherheitsfunktion.PIN_TAN_922)
|
||||
expect(decoupledPushTanMethod.tanProcess).toBe(TanProcess.TanProcess2)
|
||||
expect(decoupledPushTanMethod.technicalTanMethodIdentification).toBe("pushTAN-dec")
|
||||
expect(decoupledPushTanMethod.dkTanMethod).toBe(DkTanMethod.Decoupled)
|
||||
expect(decoupledPushTanMethod.versionDkTanMethod).toBe(null)
|
||||
expect(decoupledPushTanMethod.methodName).toBe("pushTAN 2.0")
|
||||
expect(decoupledPushTanMethod.maxTanInputLength).toBe(null)
|
||||
expect(decoupledPushTanMethod.allowedTanFormat).toBe(null)
|
||||
|
||||
expect(decoupledPushTanMethod.descriptionToShowToUser).toBe("Aufforderung")
|
||||
expect(decoupledPushTanMethod.maxReturnValueLength).toBe(2048)
|
||||
expect(decoupledPushTanMethod.multipleTansAllowed).toBe(true)
|
||||
expect(decoupledPushTanMethod.timeAndDialogRelation).toBe(TanZeitUndDialogbezug.TanZeitversetztDialoguebergreifendErlaubt)
|
||||
expect(decoupledPushTanMethod.cancellationAllowed).toBe(false)
|
||||
|
||||
expect(decoupledPushTanMethod.nameOfTanMediumRequired).toBe(BezeichnungDesTanMediumsErforderlich.BezeichnungDesTanMediumsMussAngegebenWerden)
|
||||
expect(decoupledPushTanMethod.countSupportedActiveTanMedia).toBe(2)
|
||||
|
||||
expect(decoupledPushTanMethod.maxNumberOfStateRequestsForDecoupled).toBe(180)
|
||||
expect(decoupledPushTanMethod.initialDelayInSecondsForStateRequestsForDecoupled).toBe(1)
|
||||
expect(decoupledPushTanMethod.delayInSecondsForNextStateRequestsForDecoupled).toBe(1)
|
||||
expect(decoupledPushTanMethod.manualConfirmationAllowedForDecoupled).toBe(true)
|
||||
expect(decoupledPushTanMethod.periodicStateRequestsAllowedForDecoupled).toBe(true)
|
||||
}
|
||||
?: run { fail("No segment of type TanInfo found in ${result.receivedSegments}") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseTanResponse_NoStrongAuthenticationRequired() {
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ class BanksFinTsDetailsRetriever {
|
|||
private val tanMethodTypes = mutableMapOf<TanMethodType?, MutableSet<TanMethodParameters>>()
|
||||
|
||||
private val tanMethodParameterTechnicalIdentification = mutableSetOf<String>()
|
||||
private val tanMethodParameterVersionZkaTanMethod = mutableSetOf<String?>()
|
||||
private val tanMethodParameterVersionDkTanMethod = mutableSetOf<String?>()
|
||||
|
||||
private val requiresSmsAbbuchungskonto = mutableListOf<BankInfo>()
|
||||
private val requiresAuftraggeberkonto = mutableListOf<BankInfo>()
|
||||
|
@ -220,7 +220,7 @@ class BanksFinTsDetailsRetriever {
|
|||
}
|
||||
|
||||
tanMethodParameterTechnicalIdentification.add(methodParameter.technicalTanMethodIdentification)
|
||||
tanMethodParameterVersionZkaTanMethod.add(methodParameter.versionZkaTanMethod)
|
||||
tanMethodParameterVersionDkTanMethod.add(methodParameter.versionDkTanMethod)
|
||||
|
||||
if (methodParameter.smsDebitAccountRequired == SmsAbbuchungskontoErforderlich.SmsAbbuchungskontoMussAngegebenWerden) {
|
||||
requiresSmsAbbuchungskonto.add(bankInfo)
|
||||
|
@ -283,11 +283,11 @@ class BanksFinTsDetailsRetriever {
|
|||
private fun printStatistics() {
|
||||
log.info("Did not receive response from Banks ${printBanks(requestNotSuccessful)}")
|
||||
|
||||
log.info("Mapped tanMethodTypes: ${tanMethodTypes.map { System.lineSeparator() + it.key + ": " + it.value.map { it.methodName + " " + it.zkaTanMethod + " " + it.technicalTanMethodIdentification + " (" + it.descriptionToShowToUser + ")" }.toSet().joinToString(", ") }}\n\n")
|
||||
log.info("TanMethodParameters:${tanMethodParameter.map { System.lineSeparator() + it.key + ": " + it.value.map { it.securityFunction.code + " " + it.zkaTanMethod + " " + it.technicalTanMethodIdentification + " (" + it.descriptionToShowToUser + ")" }.toSet().joinToString(", ") } }\n\n")
|
||||
log.info("Mapped tanMethodTypes: ${tanMethodTypes.map { System.lineSeparator() + it.key + ": " + it.value.map { it.methodName + " " + it.dkTanMethod + " " + it.technicalTanMethodIdentification + " (" + it.descriptionToShowToUser + ")" }.toSet().joinToString(", ") }}\n\n")
|
||||
log.info("TanMethodParameters:${tanMethodParameter.map { System.lineSeparator() + it.key + ": " + it.value.map { it.securityFunction.code + " " + it.dkTanMethod + " " + it.technicalTanMethodIdentification + " (" + it.descriptionToShowToUser + ")" }.toSet().joinToString(", ") } }\n\n")
|
||||
|
||||
log.info("TanMethodParameters TechnicalIdentification:${tanMethodParameterTechnicalIdentification.joinToString(", ") } \n\n")
|
||||
log.info("TanMethodParameters VersionZkaTanMethod:${tanMethodParameterVersionZkaTanMethod.joinToString(", ") } \n\n")
|
||||
log.info("TanMethodParameters VersionDkTanMethod:${tanMethodParameterVersionDkTanMethod.joinToString(", ") } \n\n")
|
||||
|
||||
log.info("Requires SmsAbbuchungskonto ${printBanks(requiresSmsAbbuchungskonto)}") // no (only 2)
|
||||
log.info("Requires Auftraggeberkonto ${printBanks(requiresAuftraggeberkonto)}") // yes, a lot of (12631)
|
||||
|
|
|
@ -5,6 +5,8 @@ enum class AllowedTanFormat {
|
|||
|
||||
Numeric,
|
||||
|
||||
Alphanumeric
|
||||
Alphanumeric,
|
||||
|
||||
TanIsEnteredOnOtherDevice
|
||||
|
||||
}
|
|
@ -283,10 +283,11 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
|
|||
}
|
||||
}
|
||||
|
||||
open fun mapAllowedTanFormat(allowedTanFormat: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat): AllowedTanFormat {
|
||||
open fun mapAllowedTanFormat(allowedTanFormat: net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat?): AllowedTanFormat {
|
||||
return when (allowedTanFormat) {
|
||||
net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Alphanumeric -> AllowedTanFormat.Alphanumeric
|
||||
net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Numeric -> AllowedTanFormat.Numeric
|
||||
null -> AllowedTanFormat.TanIsEnteredOnOtherDevice
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -420,10 +421,11 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
|
|||
}
|
||||
}
|
||||
|
||||
open fun mapAllowedTanFormat(allowedTanFormat: AllowedTanFormat): net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat {
|
||||
open fun mapAllowedTanFormat(allowedTanFormat: AllowedTanFormat): net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat? {
|
||||
return when (allowedTanFormat) {
|
||||
AllowedTanFormat.Alphanumeric -> net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Alphanumeric
|
||||
AllowedTanFormat.Numeric -> net.dankito.banking.fints.messages.datenelemente.implementierte.tan.AllowedTanFormat.Numeric
|
||||
AllowedTanFormat.TanIsEnteredOnOtherDevice -> null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue