mirror of
https://github.com/bitwarden/android.git
synced 2024-11-21 08:55:48 +03:00
PM-12259: Use validatePin SDK to validate the users pin (#4311)
This commit is contained in:
parent
1b0bc13903
commit
089136552b
5 changed files with 74 additions and 88 deletions
|
@ -2,7 +2,6 @@ package com.x8bit.bitwarden.data.auth.repository
|
|||
|
||||
import com.bitwarden.core.AuthRequestMethod
|
||||
import com.bitwarden.core.InitUserCryptoMethod
|
||||
import com.bitwarden.core.InitUserCryptoRequest
|
||||
import com.bitwarden.crypto.HashPurpose
|
||||
import com.bitwarden.crypto.Kdf
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
|
@ -114,7 +113,6 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.OrganizationType
|
|||
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockError
|
||||
|
@ -1249,41 +1247,17 @@ class AuthRepositoryImpl(
|
|||
?.activeAccount
|
||||
?.profile
|
||||
?: return ValidatePinResult.Error
|
||||
val privateKey = authDiskSource
|
||||
.getPrivateKey(userId = activeAccount.userId)
|
||||
?: return ValidatePinResult.Error
|
||||
val pinProtectedUserKey = authDiskSource
|
||||
.getPinProtectedUserKey(userId = activeAccount.userId)
|
||||
?: return ValidatePinResult.Error
|
||||
|
||||
// HACK: As the SDK doesn't provide a way to directly validate the pin yet, we instead
|
||||
// try to initialize the user crypto, and if it succeeds then the PIN is correct, otherwise
|
||||
// the PIN is incorrect.
|
||||
return vaultSdkSource
|
||||
.initializeCrypto(
|
||||
.validatePin(
|
||||
userId = activeAccount.userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = activeAccount.toSdkParams(),
|
||||
email = activeAccount.email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Pin(
|
||||
pin = pin,
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
),
|
||||
),
|
||||
pin = pin,
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
)
|
||||
.fold(
|
||||
onSuccess = {
|
||||
when (it) {
|
||||
InitializeCryptoResult.Success -> {
|
||||
ValidatePinResult.Success(isValid = true)
|
||||
}
|
||||
|
||||
is InitializeCryptoResult.AuthenticationError -> {
|
||||
ValidatePinResult.Success(isValid = false)
|
||||
}
|
||||
}
|
||||
},
|
||||
onSuccess = { ValidatePinResult.Success(isValid = it) },
|
||||
onFailure = { ValidatePinResult.Error },
|
||||
)
|
||||
}
|
||||
|
|
|
@ -94,6 +94,15 @@ interface VaultSdkSource {
|
|||
encryptedPin: String,
|
||||
): Result<String>
|
||||
|
||||
/**
|
||||
* Validate the user pin using the [pinProtectedUserKey].
|
||||
*/
|
||||
suspend fun validatePin(
|
||||
userId: String,
|
||||
pin: String,
|
||||
pinProtectedUserKey: String,
|
||||
): Result<Boolean>
|
||||
|
||||
/**
|
||||
* Gets the key for an auth request that is required to approve or decline it.
|
||||
*/
|
||||
|
|
|
@ -109,6 +109,17 @@ class VaultSdkSourceImpl(
|
|||
.derivePinUserKey(encryptedPin = encryptedPin)
|
||||
}
|
||||
|
||||
override suspend fun validatePin(
|
||||
userId: String,
|
||||
pin: String,
|
||||
pinProtectedUserKey: String,
|
||||
): Result<Boolean> =
|
||||
runCatchingWithLogs {
|
||||
getClient(userId = userId)
|
||||
.auth()
|
||||
.validatePin(pin = pin, pinProtectedUserKey = pinProtectedUserKey)
|
||||
}
|
||||
|
||||
override suspend fun getAuthRequestKey(
|
||||
publicKey: String,
|
||||
userId: String,
|
||||
|
|
|
@ -123,7 +123,6 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
|||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockOrganization
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockPolicy
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
|
@ -5828,33 +5827,11 @@ class AuthRepositoryTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validatePin returns ValidatePinResult Error when no private key found`() = runTest {
|
||||
val pin = "PIN"
|
||||
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
privateKey = null,
|
||||
)
|
||||
|
||||
val result = repository.validatePin(pin = pin)
|
||||
|
||||
assertEquals(
|
||||
ValidatePinResult.Error,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validatePin returns ValidatePinResult Error when no pin protected user key found`() =
|
||||
runTest {
|
||||
val pin = "PIN"
|
||||
val privateKey = "privateKey"
|
||||
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
privateKey = privateKey,
|
||||
)
|
||||
fakeAuthDiskSource.storePinProtectedUserKey(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
pinProtectedUserKey = null,
|
||||
|
@ -5869,23 +5846,19 @@ class AuthRepositoryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `validatePin returns ValidatePinResult Error when initialize crypto fails`() = runTest {
|
||||
fun `validatePin returns ValidatePinResult Error when SDK validatePin fails`() = runTest {
|
||||
val pin = "PIN"
|
||||
val privateKey = "privateKey"
|
||||
val pinProtectedUserKey = "pinProtectedUserKey"
|
||||
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
privateKey = privateKey,
|
||||
)
|
||||
fakeAuthDiskSource.storePinProtectedUserKey(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
)
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
vaultSdkSource.validatePin(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
request = any(),
|
||||
pin = pin,
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
)
|
||||
} returns Throwable().asFailure()
|
||||
|
||||
|
@ -5895,36 +5868,33 @@ class AuthRepositoryTest {
|
|||
ValidatePinResult.Error,
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.validatePin(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
request = any(),
|
||||
pin = pin,
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `validatePin returns ValidatePinResult Success with valid false when initialize cryto returns AuthenticationError`() =
|
||||
fun `validatePin returns ValidatePinResult Success with valid false when SDK validatePin returns false`() =
|
||||
runTest {
|
||||
val pin = "PIN"
|
||||
val privateKey = "privateKey"
|
||||
val pinProtectedUserKey = "pinProtectedUserKey"
|
||||
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
privateKey = privateKey,
|
||||
)
|
||||
fakeAuthDiskSource.storePinProtectedUserKey(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
)
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
vaultSdkSource.validatePin(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
request = any(),
|
||||
pin = pin,
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
)
|
||||
} returns InitializeCryptoResult.AuthenticationError().asSuccess()
|
||||
} returns false.asSuccess()
|
||||
|
||||
val result = repository.validatePin(pin = pin)
|
||||
|
||||
|
@ -5932,36 +5902,33 @@ class AuthRepositoryTest {
|
|||
ValidatePinResult.Success(isValid = false),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.validatePin(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
request = any(),
|
||||
pin = pin,
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `validatePin returns ValidatePinResult Success with valid true when initialize cryto returns Success`() =
|
||||
fun `validatePin returns ValidatePinResult Success with valid true when SDK validatePin returns true`() =
|
||||
runTest {
|
||||
val pin = "PIN"
|
||||
val privateKey = "privateKey"
|
||||
val pinProtectedUserKey = "pinProtectedUserKey"
|
||||
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
privateKey = privateKey,
|
||||
)
|
||||
fakeAuthDiskSource.storePinProtectedUserKey(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
)
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
vaultSdkSource.validatePin(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
request = any(),
|
||||
pin = pin,
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
)
|
||||
} returns InitializeCryptoResult.Success.asSuccess()
|
||||
} returns true.asSuccess()
|
||||
|
||||
val result = repository.validatePin(pin = pin)
|
||||
|
||||
|
@ -5969,10 +5936,11 @@ class AuthRepositoryTest {
|
|||
ValidatePinResult.Success(isValid = true),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.validatePin(
|
||||
userId = SINGLE_USER_STATE_1.activeUserId,
|
||||
request = any(),
|
||||
pin = pin,
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,6 +234,30 @@ class VaultSdkSourceTest {
|
|||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validatePin should call SDK and return a Result with the correct data`() =
|
||||
runBlocking {
|
||||
val userId = "userId"
|
||||
val pin = "pin"
|
||||
val pinProtectedUserKey = "pinProtectedUserKey"
|
||||
val expectedResult = true
|
||||
coEvery {
|
||||
clientAuth.validatePin(pin = pin, pinProtectedUserKey = pinProtectedUserKey)
|
||||
} returns expectedResult
|
||||
|
||||
val result = vaultSdkSource.validatePin(
|
||||
userId = userId,
|
||||
pin = pin,
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
)
|
||||
|
||||
assertEquals(expectedResult.asSuccess(), result)
|
||||
coVerify(exactly = 1) {
|
||||
clientAuth.validatePin(pin = pin, pinProtectedUserKey = pinProtectedUserKey)
|
||||
sdkClientManager.getOrCreateClient(userId = userId)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getAuthRequestKey should call SDK and return a Result with correct data`() =
|
||||
runBlocking {
|
||||
|
|
Loading…
Reference in a new issue