mirror of
https://github.com/bitwarden/android.git
synced 2025-03-15 10:48:47 +03:00
Add TOTP code generation (#687)
This commit is contained in:
parent
88e4b45f7d
commit
a877897a19
7 changed files with 132 additions and 0 deletions
|
@ -5,6 +5,7 @@ import com.bitwarden.core.CipherListView
|
|||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.Collection
|
||||
import com.bitwarden.core.CollectionView
|
||||
import com.bitwarden.core.DateTime
|
||||
import com.bitwarden.core.DerivePinKeyResponse
|
||||
import com.bitwarden.core.Folder
|
||||
import com.bitwarden.core.FolderView
|
||||
|
@ -15,6 +16,7 @@ import com.bitwarden.core.PasswordHistory
|
|||
import com.bitwarden.core.PasswordHistoryView
|
||||
import com.bitwarden.core.Send
|
||||
import com.bitwarden.core.SendView
|
||||
import com.bitwarden.core.TotpResponse
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
|
||||
|
||||
/**
|
||||
|
@ -251,4 +253,13 @@ interface VaultSdkSource {
|
|||
userId: String,
|
||||
passwordHistoryList: List<PasswordHistory>,
|
||||
): Result<List<PasswordHistoryView>>
|
||||
|
||||
/**
|
||||
* Generate a verification code and the period using the totp code.
|
||||
*/
|
||||
suspend fun generateTotp(
|
||||
userId: String,
|
||||
totp: String,
|
||||
time: DateTime,
|
||||
): Result<TotpResponse>
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.bitwarden.core.CipherListView
|
|||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.Collection
|
||||
import com.bitwarden.core.CollectionView
|
||||
import com.bitwarden.core.DateTime
|
||||
import com.bitwarden.core.DerivePinKeyResponse
|
||||
import com.bitwarden.core.Folder
|
||||
import com.bitwarden.core.FolderView
|
||||
|
@ -14,6 +15,7 @@ import com.bitwarden.core.PasswordHistory
|
|||
import com.bitwarden.core.PasswordHistoryView
|
||||
import com.bitwarden.core.Send
|
||||
import com.bitwarden.core.SendView
|
||||
import com.bitwarden.core.TotpResponse
|
||||
import com.bitwarden.sdk.BitwardenException
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.bitwarden.sdk.ClientVault
|
||||
|
@ -252,6 +254,19 @@ class VaultSdkSourceImpl(
|
|||
.decryptList(passwordHistoryList)
|
||||
}
|
||||
|
||||
override suspend fun generateTotp(
|
||||
userId: String,
|
||||
totp: String,
|
||||
time: DateTime,
|
||||
): Result<TotpResponse> = runCatching {
|
||||
getClient(userId = userId)
|
||||
.vault()
|
||||
.generateTotp(
|
||||
key = totp,
|
||||
time = time,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getClient(
|
||||
userId: String,
|
||||
): Client = sdkClientManager.getOrCreateClient(userId = userId)
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.vault.repository
|
|||
import android.net.Uri
|
||||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.CollectionView
|
||||
import com.bitwarden.core.DateTime
|
||||
import com.bitwarden.core.FolderView
|
||||
import com.bitwarden.core.SendType
|
||||
import com.bitwarden.core.SendView
|
||||
|
@ -13,6 +14,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
|
|||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ShareCipherResult
|
||||
|
@ -198,6 +200,11 @@ interface VaultRepository : VaultLockManager {
|
|||
*/
|
||||
suspend fun removePasswordSend(sendId: String): RemovePasswordSendResult
|
||||
|
||||
/**
|
||||
* Attempt to get the verification code and the period.
|
||||
*/
|
||||
suspend fun generateTotp(totpCode: String, time: DateTime): GenerateTotpResult
|
||||
|
||||
/**
|
||||
* Attempt to delete a send.
|
||||
*/
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.vault.repository
|
|||
import android.net.Uri
|
||||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.CollectionView
|
||||
import com.bitwarden.core.DateTime
|
||||
import com.bitwarden.core.FolderView
|
||||
import com.bitwarden.core.InitOrgCryptoRequest
|
||||
import com.bitwarden.core.InitUserCryptoMethod
|
||||
|
@ -37,6 +38,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
|
|||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ShareCipherResult
|
||||
|
@ -593,6 +595,27 @@ class VaultRepositoryImpl(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun generateTotp(
|
||||
totpCode: String,
|
||||
time: DateTime,
|
||||
): GenerateTotpResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
return vaultSdkSource.generateTotp(
|
||||
time = time,
|
||||
userId = userId,
|
||||
totp = totpCode,
|
||||
)
|
||||
.fold(
|
||||
onSuccess = {
|
||||
GenerateTotpResult.Success(
|
||||
code = it.code,
|
||||
periodSeconds = it.period.toInt(),
|
||||
)
|
||||
},
|
||||
onFailure = { GenerateTotpResult.Error },
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given [userId] has an associated encrypted PIN key but not a pin-protected user
|
||||
* key. This indicates a scenario in which a user has requested PIN unlocking but requires
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
/**
|
||||
* Models the result of generating a totp code.
|
||||
*/
|
||||
sealed class GenerateTotpResult {
|
||||
|
||||
/**
|
||||
* The code was generated successfully.
|
||||
*/
|
||||
data class Success(
|
||||
val code: String,
|
||||
val periodSeconds: Int,
|
||||
) : GenerateTotpResult()
|
||||
|
||||
/**
|
||||
* An error occurred while generating the code.
|
||||
*/
|
||||
data object Error : GenerateTotpResult()
|
||||
}
|
|
@ -5,6 +5,7 @@ import com.bitwarden.core.CipherListView
|
|||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.Collection
|
||||
import com.bitwarden.core.CollectionView
|
||||
import com.bitwarden.core.DateTime
|
||||
import com.bitwarden.core.DerivePinKeyResponse
|
||||
import com.bitwarden.core.Folder
|
||||
import com.bitwarden.core.FolderView
|
||||
|
@ -14,6 +15,7 @@ import com.bitwarden.core.PasswordHistory
|
|||
import com.bitwarden.core.PasswordHistoryView
|
||||
import com.bitwarden.core.Send
|
||||
import com.bitwarden.core.SendView
|
||||
import com.bitwarden.core.TotpResponse
|
||||
import com.bitwarden.sdk.BitwardenException
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.bitwarden.sdk.ClientCrypto
|
||||
|
@ -31,6 +33,7 @@ import io.mockk.mockk
|
|||
import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
|
@ -616,4 +619,32 @@ class VaultSdkSourceTest {
|
|||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generateTotp should call SDK and return a Result with correct data`() =
|
||||
runTest {
|
||||
val userId = "userId"
|
||||
val totpResponse = TotpResponse("TestCode", 30u)
|
||||
coEvery { clientVault.generateTotp(any(), any()) } returns totpResponse
|
||||
|
||||
val time = DateTime.now()
|
||||
val result = vaultSdkSource.generateTotp(
|
||||
userId = userId,
|
||||
totp = "Totp",
|
||||
time = time,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
Result.success(totpResponse),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
clientVault.generateTotp(
|
||||
key = "Totp",
|
||||
time = time,
|
||||
)
|
||||
}
|
||||
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,14 @@ import android.net.Uri
|
|||
import app.cash.turbine.test
|
||||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.CollectionView
|
||||
import com.bitwarden.core.DateTime
|
||||
import com.bitwarden.core.FolderView
|
||||
import com.bitwarden.core.InitOrgCryptoRequest
|
||||
import com.bitwarden.core.InitUserCryptoMethod
|
||||
import com.bitwarden.core.InitUserCryptoRequest
|
||||
import com.bitwarden.core.SendType
|
||||
import com.bitwarden.core.SendView
|
||||
import com.bitwarden.core.TotpResponse
|
||||
import com.bitwarden.crypto.Kdf
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
|
||||
|
@ -57,6 +59,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
|
|||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ShareCipherResult
|
||||
|
@ -2107,6 +2110,28 @@ class VaultRepositoryTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generateTotp should return a success result on getting a code`() = runTest {
|
||||
val totpResponse = TotpResponse("Testcode", 30u)
|
||||
coEvery { vaultSdkSource.generateTotp(any(), any(), any()) } returns Result.success(
|
||||
totpResponse,
|
||||
)
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
val result = vaultRepository.generateTotp(
|
||||
totpCode = "testCode",
|
||||
time = DateTime.now(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
GenerateTotpResult.Success(
|
||||
code = totpResponse.code,
|
||||
periodSeconds = totpResponse.period.toInt(),
|
||||
),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
//region Helper functions
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue