Skip to content
Snippets Groups Projects
Unverified Commit 4dc73f1a authored by Thomas Klingbeil's avatar Thomas Klingbeil Committed by GitHub
Browse files

Revert "Extend teleTAN to 10 characters + input validation (#252)" (#267)

This reverts commit 8577f8d4.
parent f1377e0e
No related branches found
No related tags found
No related merge requests found
Showing
with 22 additions and 364 deletions
......@@ -5,7 +5,6 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import de.rki.coronawarnapp.storage.SubmissionRepository
import de.rki.coronawarnapp.util.TanHelper
class SubmissionTanViewModel : ViewModel() {
......@@ -17,21 +16,7 @@ class SubmissionTanViewModel : ViewModel() {
val isValidTanFormat =
Transformations.map(tan) {
it != null &&
it.length == TanConstants.MAX_LENGTH &&
TanHelper.isChecksumValid(it) &&
TanHelper.allCharactersValid(it)
}
val tanChecksumValid =
Transformations.map(tan) {
((it !== null && it.trim().length == TanConstants.MAX_LENGTH) &&
TanHelper.isChecksumValid(it).not()).not()
}
val tanCharactersValid =
Transformations.map(tan) {
!((it != null) && TanHelper.allCharactersValid(it).not())
it != null && it.length == TanConstants.MAX_LENGTH
}
fun storeTeletan() {
......
package de.rki.coronawarnapp.ui.submission
object TanConstants {
const val MAX_LENGTH = 10
const val MAX_LENGTH = 7
val ALPHA_NUMERIC_CHARS = ('a'..'z').plus('A'..'Z').plus('0'..'9')
}
......@@ -8,9 +8,6 @@ import android.view.inputmethod.InputMethodManager
import android.widget.FrameLayout
import androidx.core.widget.doOnTextChanged
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.ui.submission.TanConstants.MAX_LENGTH
import de.rki.coronawarnapp.util.TanHelper
import java.util.Locale
import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_edittext
import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_1
import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_2
......@@ -19,11 +16,6 @@ import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_4
import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_5
import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_6
import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_7
import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_8
import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_9
import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_10
import kotlinx.android.synthetic.main.view_tan_input.view.dash_view_1
import kotlinx.android.synthetic.main.view_tan_input.view.dash_view_2
class TanInput(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs) {
......@@ -38,7 +30,7 @@ class TanInput(context: Context, attrs: AttributeSet) : FrameLayout(context, att
TanConstants.ALPHA_NUMERIC_CHARS.contains(it)
}
}
private var lengthFilter = InputFilter.LengthFilter(MAX_LENGTH)
private val lengthFilter = InputFilter.LengthFilter(TanConstants.MAX_LENGTH)
var listener: ((String?) -> Unit)? = null
......@@ -49,9 +41,6 @@ class TanInput(context: Context, attrs: AttributeSet) : FrameLayout(context, att
tan_input_edittext.filters = arrayOf(whitespaceFilter, alphaNumericFilter, lengthFilter)
dash_view_1.text = "-"
dash_view_2.text = "-"
// register listener
tan_input_edittext.doOnTextChanged { text, _, _, _ -> updateTan(text) }
setOnClickListener { showKeyboard() }
......@@ -67,20 +56,9 @@ class TanInput(context: Context, attrs: AttributeSet) : FrameLayout(context, att
}
}
private fun limitLength(length: Int?) {
lengthFilter = InputFilter.LengthFilter(if (length != null) length else MAX_LENGTH)
tan_input_edittext.filters = arrayOf(whitespaceFilter, alphaNumericFilter, lengthFilter)
}
private fun updateTan(text: CharSequence?) {
this.tan = text?.toString()?.toUpperCase(Locale.getDefault())
this.tan = text?.toString()
updateDigits()
tan?.let {
limitLength(
if (TanHelper.allCharactersValid(it)) null
else it.length
)
}
notifyListener()
}
......@@ -93,24 +71,9 @@ class TanInput(context: Context, attrs: AttributeSet) : FrameLayout(context, att
tan_input_textview_4,
tan_input_textview_5,
tan_input_textview_6,
tan_input_textview_7,
tan_input_textview_8,
tan_input_textview_9,
tan_input_textview_10
tan_input_textview_7
).forEachIndexed { i, tanDigit ->
tanDigit.text = digitAtIndex(i)
tanDigit.background =
if (digitAtIndex(i) == "")
resources.getDrawable(R.drawable.tan_input_digit, null)
else if (TanHelper.isTanCharacterValid(digitAtIndex(i)))
resources.getDrawable(R.drawable.tan_input_digit_entered, null)
else resources.getDrawable(R.drawable.tan_input_digit_error, null)
tanDigit.setTextColor(
if (TanHelper.isTanCharacterValid(digitAtIndex(i)))
resources.getColor(R.color.colorTextSemanticNeutral, null)
else resources.getColor(R.color.colorTextSemanticRed, null)
)
}
private fun digitAtIndex(index: Int): String = tan?.getOrNull(index)?.toString() ?: ""
......
package de.rki.coronawarnapp.util
import de.rki.coronawarnapp.ui.submission.TanConstants.MAX_LENGTH
import java.security.MessageDigest
import java.util.Locale
object TanHelper {
private const val VALID_CHARACTERS = "23456789ABCDEFGHJKMNPQRSTUVWXYZ"
fun isChecksumValid(tan: String): Boolean {
if (tan.trim().length != MAX_LENGTH)
return false
val subTan = tan.substring(0, MAX_LENGTH - 1).toUpperCase(Locale.getDefault())
val tanDigest = MessageDigest.getInstance("SHA-256").digest(subTan.toByteArray())
var checkChar = "%02x".format(tanDigest[0])[0]
if (checkChar == '0') checkChar = 'G'
if (checkChar == '1') checkChar = 'H'
return checkChar.toUpperCase() == tan.last().toUpperCase()
}
fun allCharactersValid(tan: String): Boolean {
for (character in tan) {
if (!isTanCharacterValid(character.toString()))
return false
}
return true
}
fun isTanCharacterValid(character: String): Boolean {
return VALID_CHARACTERS.contains(character)
}
}
......@@ -193,8 +193,3 @@ fun formatShowRiskStatusCard(deviceUiState: DeviceUIState?): Int =
deviceUiState != DeviceUIState.PAIRED_POSITIVE_TELETAN &&
deviceUiState != DeviceUIState.SUBMITTED_FINAL
)
fun formatShowTanCharacterError(
charactersValid: Boolean,
checksumValid: Boolean
): Int = formatVisibility(checksumValid && !charactersValid)
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/submission_tan_input_digit_radius" />
<solid android:color="@color/colorSurface2" />
</shape>
</item>
<item
android:height="1.5dp"
android:gravity="bottom">
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:bottomLeftRadius="@dimen/submission_tan_input_digit_radius" />
<corners android:bottomRightRadius="@dimen/submission_tan_input_digit_radius" />
<solid android:color="@color/colorTextSemanticNeutral" />
</shape>
</item>
</layer-list>
\ No newline at end of file
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/submission_tan_input_digit_radius" />
<gradient
android:angle="90"
android:centerColor="@color/colorSurface2"
android:centerX="0.02"
android:endColor="@color/colorSurface2"
android:startColor="@color/colorTextSemanticNeutral" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/submission_tan_input_digit_radius" />
<solid android:color="@color/colorSurface2" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/submission_tan_input_digit_radius" />
<solid android:color="@color/colorSurface2" />
</shape>
</item>
<item
android:height="1.5dp"
android:gravity="bottom">
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:bottomLeftRadius="@dimen/submission_tan_input_digit_radius" />
<corners android:bottomRightRadius="@dimen/submission_tan_input_digit_radius" />
<solid android:color="@color/colorTextSemanticRed" />
</shape>
</item>
</layer-list>
......@@ -5,7 +5,6 @@
<data>
<import type="de.rki.coronawarnapp.util.formatter.FormatterHelper" />
<import type="de.rki.coronawarnapp.util.formatter.FormatterSubmissionHelper" />
<variable
......@@ -55,33 +54,6 @@
app:layout_constraintStart_toStartOf="@+id/guideline_start"
app:layout_constraintTop_toBottomOf="@+id/submission_tan_body" />
<TextView
android:id="@+id/submission_tan_character_error"
style="@style/subtitle"
android:layout_width="@dimen/match_constraint"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:visibility="@{FormatterSubmissionHelper.formatShowTanCharacterError(viewmodel.tanCharactersValid, viewmodel.tanChecksumValid)}"
android:text="@string/submission_tan_character_error"
android:textColor="@color/colorTextSemanticRed"
app:layout_constraintEnd_toStartOf="@+id/guideline_end"
app:layout_constraintStart_toStartOf="@+id/guideline_start"
app:layout_constraintTop_toBottomOf="@id/submission_tan_input" />
<TextView
android:id="@+id/submission_tan_error"
style="@style/subtitle"
android:layout_width="@dimen/match_constraint"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:visibility="@{FormatterHelper.formatVisibility(!viewmodel.tanChecksumValid)}"
android:text="@string/submission_tan_error"
android:textColor="@color/colorTextSemanticRed"
app:layout_constraintEnd_toStartOf="@+id/guideline_end"
app:layout_constraintStart_toStartOf="@+id/guideline_start"
app:layout_constraintTop_toBottomOf="@id/submission_tan_input" />
<Button
android:id="@+id/submission_tan_button_enter"
style="@style/buttonPrimary"
......
......@@ -36,26 +36,17 @@
android:id="@+id/tan_input_textview_3"
style="@style/tanInputDigit"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/dash_view_1"
app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_4"
app:layout_constraintStart_toEndOf="@+id/tan_input_textview_2"
app:layout_constraintTop_toTopOf="parent"
tools:text="X" />
<TextView
android:id="@+id/dash_view_1"
style="@style/tanInputDash"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_4"
app:layout_constraintStart_toEndOf="@+id/tan_input_textview_3"
app:layout_constraintTop_toTopOf="parent"
tools:text="-" />
<TextView
android:id="@+id/tan_input_textview_4"
style="@style/tanInputDigit"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_5"
app:layout_constraintStart_toEndOf="@+id/dash_view_1"
app:layout_constraintStart_toEndOf="@+id/tan_input_textview_3"
app:layout_constraintTop_toTopOf="parent"
tools:text="X" />
......@@ -72,54 +63,19 @@
android:id="@+id/tan_input_textview_6"
style="@style/tanInputDigit"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/dash_view_2"
app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_7"
app:layout_constraintStart_toEndOf="@+id/tan_input_textview_5"
app:layout_constraintTop_toTopOf="parent"
tools:text="X" />
<TextView
android:id="@+id/dash_view_2"
style="@style/tanInputDash"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_7"
app:layout_constraintStart_toEndOf="@+id/tan_input_textview_6"
app:layout_constraintTop_toTopOf="parent"
tools:text="-" />
<TextView
android:id="@+id/tan_input_textview_7"
style="@style/tanInputDigit"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_8"
app:layout_constraintStart_toEndOf="@+id/dash_view_2"
app:layout_constraintTop_toTopOf="parent"
tools:text="X" />
<TextView
android:id="@+id/tan_input_textview_8"
style="@style/tanInputDigit"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_9"
app:layout_constraintStart_toEndOf="@+id/tan_input_textview_7"
app:layout_constraintTop_toTopOf="parent"
tools:text="X" />
<TextView
android:id="@+id/tan_input_textview_9"
style="@style/tanInputDigit"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_10"
app:layout_constraintStart_toEndOf="@+id/tan_input_textview_8"
app:layout_constraintStart_toEndOf="@+id/tan_input_textview_6"
app:layout_constraintTop_toTopOf="parent"
tools:text="X" />
<TextView
android:id="@+id/tan_input_textview_10"
style="@style/tanInputDigit"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/tan_input_textview_9"
app:layout_constraintTop_toTopOf="parent"
tools:text="X" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
......@@ -81,10 +81,8 @@
<!-- Submission Tan Input -->
<dimen name="submission_tan_input_edittext_size">1dp</dimen>
<dimen name="submission_tan_input_digit_radius">2dp</dimen>
<dimen name="submission_tan_input_digit_width">22dp</dimen>
<dimen name="submission_tan_input_digit_height">40dp</dimen>
<dimen name="submission_tan_input_dash_width">11dp</dimen>
<dimen name="submission_tan_input_dash_height">40dp</dimen>
<dimen name="submission_tan_input_digit_width">24dp</dimen>
<dimen name="submission_tan_input_digit_height">32dp</dimen>
<!-- Submission QR Code Scan -->
<dimen name="submission_scan_qr_code_viewfinder_size">240dp</dimen>
......
......@@ -2664,13 +2664,9 @@ as modifying the License.
<!-- XHED: Page title for TAN submission pge -->
<string name="submission_tan_title">TAN Eingabe</string>
<!-- YTXT: Body text for the tan submission page -->
<string name="submission_tan_body">Geben Sie bitte die 10 Stellen der TAN ein, die Ihnen mitgeteilt wurde.</string>
<string name="submission_tan_body">Die TAN ist 7-stellig und Groß- und Kleinschreibung muss beachtet werden.\n\nGeben Sie bitte die Ihnen mitgeteilte TAN ein:</string>
<!-- XBUT: Submit TAN button -->
<string name="submission_tan_button_text">Weiter</string>
<!-- YTXT: Error text for the tan submission page -->
<string name="submission_tan_error">Ungültige TAN, bitte überprüfen Sie Ihre Eingabe.</string>
<!-- YTXT: Error text for the tan submission page (wrong characters) -->
<string name="submission_tan_character_error">Ungültige Eingabe, bitte überprüfen Sie das Zeichen.</string>
<!-- Submission Intro -->
<!-- XHED: Page title for menu at the start of the submission process -->
......
......@@ -216,13 +216,6 @@
<item name="android:gravity">center</item>
</style>
<style name="tanInputDash" parent="headline6">
<item name="android:layout_width">@dimen/submission_tan_input_dash_width</item>
<item name="android:layout_height">@dimen/submission_tan_input_dash_height</item>
<item name="android:background">@null</item>
<item name="android:gravity">center</item>
</style>
<style name="tanInputEdittext">
<item name="android:layout_width">@dimen/submission_tan_input_edittext_size</item>
<item name="android:layout_height">@dimen/submission_tan_input_edittext_size</item>
......
package de.rki.coronawarnapp.ui.submission
import de.rki.coronawarnapp.util.TanHelper
import org.junit.Test
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
class SubmissionTanViewModelTest {
private var viewModel: SubmissionTanViewModel = SubmissionTanViewModel()
@Test
fun allCharactersValid() {
viewModel.tan.postValue("ABCD")
viewModel.tanCharactersValid.value?.let { assertTrue(it) }
viewModel.tan.postValue("ABCD0")
viewModel.tanCharactersValid.value?.let { assertFalse(it) }
}
@Test
fun tanFormatValid() {
viewModel.tan.postValue("ZWFPC7NG47")
viewModel.tanCharactersValid.value?.let { assertTrue(it) }
viewModel.tan.postValue("ABC")
viewModel.tanCharactersValid.value?.let { assertFalse(it) }
viewModel.tan.postValue("ZWFPC7NG48")
viewModel.tanCharactersValid.value?.let { assertFalse(it) }
viewModel.tan.postValue("ZWFPC7NG4A")
viewModel.tanCharactersValid.value?.let { assertFalse(it) }
}
@Test
fun checksumValid() {
viewModel.tan.postValue("ZWFPC7NG47")
viewModel.tanCharactersValid.value?.let { assertTrue(it) }
viewModel.tan.postValue("ZWFPC7NG48")
viewModel.tanCharactersValid.value?.let { assertFalse(it) }
}
}
\ No newline at end of file
package de.rki.coronawarnapp.util
import de.rki.coronawarnapp.service.submission.SubmissionService
import org.hamcrest.CoreMatchers
import org.hamcrest.MatcherAssert
import org.junit.Test
class TanHelperTest {
@Test
fun isValidCharacter() {
// valid
val validCharacters = arrayOf(
"2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H",
"J","K","M","N","P","Q","R","S","T","U","V","W","X","Y","Z"
)
for (character in validCharacters) {
MatcherAssert.assertThat(
TanHelper.isTanCharacterValid(character),
CoreMatchers.equalTo(true)
)
}
// invalid
val invalidCharacters = arrayOf(
"0","1","O","L","I","Ö","*","&","-","a","b",
"c","ö","ß","é","."," ","€","(",")",";",","
)
for (character in invalidCharacters) {
MatcherAssert.assertThat(
TanHelper.isTanCharacterValid(character),
CoreMatchers.equalTo(false)
)
}
}
@Test
fun areCharactersValid() {
// valid input strings (not necessarily valid TANs)
val validStrings = arrayOf(
"ABCD", "2345", "PTPHM35RP4", "AAAAAAAAAA", "BBBBB")
for (text in validStrings) {
MatcherAssert.assertThat(
TanHelper.allCharactersValid(text),
CoreMatchers.equalTo(true)
)
}
// invalid input strings
val invalidStrings = arrayOf(
"ABCDÖ", "01234", "PTPHM15RP4", "AAAAAA AAA", "BB.BBB")
for (text in invalidStrings) {
MatcherAssert.assertThat(
TanHelper.allCharactersValid(text),
CoreMatchers.equalTo(false)
)
}
}
@Test
fun isChecksumValid() {
// valid
val validTans = arrayOf(
"9A3B578UMG", "DEU7TKSV3H", "PTPHM35RP4", "V923D59AT8", "H9NC5CQ34E")
for (tan in validTans) {
MatcherAssert.assertThat(
TanHelper.isChecksumValid(tan),
CoreMatchers.equalTo(true)
)
}
// invalid
val invalidTans = arrayOf(
"DEU7TKSV32", "DEU7TKSV33", "DEU7TKSV34", "DEU7TKSV35",
"DEU7TKSV36", "DEU7TKSV37", "DEU7TKSV38", "DEU7TKSV39",
"DEU7TKSV3A", "DEU7TKSV3B", "DEU7TKSV3C", "DEU7TKSV3D",
"DEU7TKSV3E", "DEU7TKSV3F", "DEU7TKSV3G",
" QV5FQ38MA",
"9A3B578UM0", "DEU7TKSV31", "Q4XBJCB43", "929B96CA8"
)
for (tan in invalidTans) {
MatcherAssert.assertThat(
TanHelper.isChecksumValid(tan),
CoreMatchers.equalTo(false)
)
}
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment