Make sure pins up to the max length of 16 digits are supported.

Bug: 284405869
Test: unit tests
Change-Id: I9b6ebadde4aa430c78438b6f029b656edf7ebaa0
This commit is contained in:
Mike Schneider
2023-05-26 11:49:51 +02:00
parent 4136968d63
commit cb71190b6f
3 changed files with 65 additions and 6 deletions

View File

@@ -16,6 +16,7 @@
package com.android.systemui.authentication.domain.interactor
import android.app.admin.DevicePolicyManager
import com.android.systemui.authentication.data.repository.AuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.dagger.SysUISingleton
@@ -177,15 +178,21 @@ constructor(
/**
* Returns a PIN code from the given list. It's assumed the given list elements are all
* [Int].
* [Int] in the range [0-9].
*/
private fun List<Any>.asCode(): Int? {
if (isEmpty()) {
private fun List<Any>.asCode(): Long? {
if (isEmpty() || size > DevicePolicyManager.MAX_PASSWORD_LENGTH) {
return null
}
var code = 0
map { it as Int }.forEach { integer -> code = code * 10 + integer }
var code = 0L
map {
require(it is Int && it in 0..9) {
"Pin is required to be Int in range [0..9], but got $it"
}
it
}
.forEach { integer -> code = code * 10 + integer }
return code
}

View File

@@ -32,7 +32,13 @@ sealed class AuthenticationMethodModel(
/** The most basic authentication method. The lock screen can be swiped away when displayed. */
object Swipe : AuthenticationMethodModel(isSecure = false)
data class Pin(val code: Int) : AuthenticationMethodModel(isSecure = true)
/**
* Authentication method using a PIN.
*
* In practice, a pin is restricted to 16 decimal digits , see
* [android.app.admin.DevicePolicyManager.MAX_PASSWORD_LENGTH]
*/
data class Pin(val code: Long) : AuthenticationMethodModel(isSecure = true)
data class Password(val password: String) : AuthenticationMethodModel(isSecure = true)

View File

@@ -16,6 +16,7 @@
package com.android.systemui.authentication.domain.interactor
import android.app.admin.DevicePolicyManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.AuthenticationRepository
@@ -168,6 +169,51 @@ class AuthenticationInteractorTest : SysuiTestCase() {
assertThat(failedAttemptCount).isEqualTo(1)
}
@Test
fun authenticate_withEmptyPin_returnsFalseAndDoesNotUnlockDevice() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf())).isFalse()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(1)
}
@Test
fun authenticate_withCorrectMaxLengthPin_returnsTrueAndUnlocksDevice() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(9999999999999999))
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(List(16) { 9 })).isTrue()
assertThat(isUnlocked).isTrue()
assertThat(failedAttemptCount).isEqualTo(0)
}
@Test
fun authenticate_withCorrectTooLongPin_returnsFalseAndDoesNotUnlockDevice() =
testScope.runTest {
// Max pin length is 16 digits. To avoid issues with overflows, this test ensures
// that all pins > 16 decimal digits are rejected.
// If the policy changes, there is work to do in SysUI.
assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17)
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(99999999999999999))
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(List(17) { 9 })).isFalse()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(1)
}
@Test
fun authenticate_withCorrectPassword_returnsTrueAndUnlocksDevice() =
testScope.runTest {