mirror of
https://github.com/bitwarden/android.git
synced 2026-10-21 10:40:49 -05:00
PM-31118: feat: Add support for SDK pin unlock (#7289)
This commit is contained in:
1 parent
4fe56cabc1
commit
7bb2247e0e
7 files changed
+292
-84
No files matched your search
+33
-19
@@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.vault.repository
|
||||
|
||||
import com.bitwarden.core.InitUserCryptoMethod
|
||||
import com.bitwarden.core.data.manager.dispatcher.DispatcherManager
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.core.data.repository.error.MissingPropertyException
|
||||
import com.bitwarden.core.data.repository.model.DataState
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
@@ -22,6 +23,7 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
|
||||
import com.x8bit.bitwarden.data.autofill.util.login
|
||||
import com.x8bit.bitwarden.data.platform.error.NoActiveUserException
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.util.isActive
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
@@ -70,7 +72,7 @@ import javax.crypto.Cipher
|
||||
* Default implementation of [VaultRepository].
|
||||
*/
|
||||
@Suppress("TooManyFunctions", "LongParameterList")
|
||||
class VaultRepositoryImpl(
|
||||
internal class VaultRepositoryImpl(
|
||||
private val vaultDiskSource: VaultDiskSource,
|
||||
private val vaultSdkSource: VaultSdkSource,
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
@@ -82,6 +84,7 @@ class VaultRepositoryImpl(
|
||||
private val vaultSyncManager: VaultSyncManager,
|
||||
private val credentialExchangeImportManager: CredentialExchangeImportManager,
|
||||
private val pinProtectedUserKeyManager: PinProtectedUserKeyManager,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
dispatcherManager: DispatcherManager,
|
||||
) : VaultRepository,
|
||||
CipherManager by cipherManager,
|
||||
@@ -371,28 +374,16 @@ class VaultRepositoryImpl(
|
||||
override suspend fun unlockVaultWithPin(
|
||||
pin: String,
|
||||
): VaultUnlockResult {
|
||||
val userId = activeUserId
|
||||
?: return VaultUnlockResult.InvalidStateError(error = NoActiveUserException())
|
||||
val userId = activeUserId ?: return VaultUnlockResult.InvalidStateError(
|
||||
error = NoActiveUserException(),
|
||||
)
|
||||
|
||||
return authDiskSource.getPinProtectedUserKeyEnvelope(userId = userId)
|
||||
?.let { pinProtectedUserKeyEnvelope ->
|
||||
this.unlockVaultForUser(
|
||||
userId = userId,
|
||||
initUserCryptoMethod = InitUserCryptoMethod.PinEnvelope(
|
||||
pin = pin,
|
||||
pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope,
|
||||
),
|
||||
)
|
||||
}
|
||||
?: run {
|
||||
return authDiskSource
|
||||
.getPinProtectedUserKey(userId = userId)
|
||||
?.let { pinProtectedUserKey ->
|
||||
// This is needed to support unlocking with a legacy pin protected user key.
|
||||
// Once the vault is unlocked, the user's pin protected user key is migrated to
|
||||
// a pin protected user key envelope.
|
||||
val pinProtectedUserKey = authDiskSource.getPinProtectedUserKey(userId = userId)
|
||||
?: return VaultUnlockResult.InvalidStateError(
|
||||
error = MissingPropertyException("Pin protected key"),
|
||||
)
|
||||
|
||||
this.unlockVaultForUser(
|
||||
userId = userId,
|
||||
initUserCryptoMethod = InitUserCryptoMethod.Pin(
|
||||
@@ -401,6 +392,29 @@ class VaultRepositoryImpl(
|
||||
),
|
||||
)
|
||||
}
|
||||
?: run {
|
||||
if (featureFlagManager.getFeatureFlag(key = FlagKey.SdkPinUnlock)) {
|
||||
this.unlockVaultForUser(
|
||||
userId = userId,
|
||||
initUserCryptoMethod = InitUserCryptoMethod.PinState(pin = pin),
|
||||
)
|
||||
} else {
|
||||
authDiskSource
|
||||
.getPinProtectedUserKeyEnvelope(userId = userId)
|
||||
?.let { pinProtectedUserKeyEnvelope ->
|
||||
this.unlockVaultForUser(
|
||||
userId = userId,
|
||||
initUserCryptoMethod = InitUserCryptoMethod.PinEnvelope(
|
||||
pin = pin,
|
||||
pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope,
|
||||
),
|
||||
)
|
||||
}
|
||||
?: VaultUnlockResult.InvalidStateError(
|
||||
error = MissingPropertyException("Pin protected key envelope"),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun generateTotp(
|
||||
|
||||
+3
@@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.vault.repository.di
|
||||
|
||||
import com.bitwarden.core.data.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.manager.CipherManager
|
||||
@@ -42,6 +43,7 @@ object VaultRepositoryModule {
|
||||
vaultSyncManager: VaultSyncManager,
|
||||
credentialExchangeImportManager: CredentialExchangeImportManager,
|
||||
pinProtectedUserKeyManager: PinProtectedUserKeyManager,
|
||||
featureFlagManager: FeatureFlagManager,
|
||||
): VaultRepository = VaultRepositoryImpl(
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
@@ -55,5 +57,6 @@ object VaultRepositoryModule {
|
||||
vaultSyncManager = vaultSyncManager,
|
||||
credentialExchangeImportManager = credentialExchangeImportManager,
|
||||
pinProtectedUserKeyManager = pinProtectedUserKeyManager,
|
||||
featureFlagManager = featureFlagManager,
|
||||
)
|
||||
}
|
||||
+239
-65
@@ -6,6 +6,7 @@ import com.bitwarden.core.InitUserCryptoMethod
|
||||
import com.bitwarden.core.MasterPasswordUnlockData
|
||||
import com.bitwarden.core.data.manager.dispatcher.DispatcherManager
|
||||
import com.bitwarden.core.data.manager.dispatcher.FakeDispatcherManager
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.core.data.repository.error.MissingPropertyException
|
||||
import com.bitwarden.core.data.repository.model.DataState
|
||||
import com.bitwarden.core.data.util.asFailure
|
||||
@@ -35,6 +36,7 @@ import com.x8bit.bitwarden.data.auth.datasource.sdk.util.toKdfRequestModel
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.createMockWrappedAccountCryptographicState
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
|
||||
import com.x8bit.bitwarden.data.platform.error.NoActiveUserException
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockAccount
|
||||
@@ -118,8 +120,11 @@ class VaultRepositoryTest {
|
||||
private val pinProtectedUserKeyManager: PinProtectedUserKeyManager = mockk {
|
||||
coEvery { deriveTemporaryPinProtectedUserKeyIfNecessary(userId = any()) } just runs
|
||||
}
|
||||
private val featureFlagManager: FeatureFlagManager = mockk {
|
||||
every { getFeatureFlag(FlagKey.SdkPinUnlock) } returns true
|
||||
}
|
||||
|
||||
private val vaultRepository = VaultRepositoryImpl(
|
||||
private val vaultRepository: VaultRepository = VaultRepositoryImpl(
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
authDiskSource = fakeAuthDiskSource,
|
||||
@@ -132,6 +137,7 @@ class VaultRepositoryTest {
|
||||
vaultSyncManager = vaultSyncManager,
|
||||
credentialExchangeImportManager = credentialExchangeImportManager,
|
||||
pinProtectedUserKeyManager = pinProtectedUserKeyManager,
|
||||
featureFlagManager = featureFlagManager,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
@@ -669,8 +675,9 @@ class VaultRepositoryTest {
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unlockVaultWithPin with missing pin-protected user key should return InvalidStateError`() =
|
||||
fun `unlockVaultWithPin with SdkPinUnlock disabled and no pin keys stored should return InvalidStateError`() =
|
||||
runTest {
|
||||
every { featureFlagManager.getFeatureFlag(FlagKey.SdkPinUnlock) } returns false
|
||||
fakeAuthDiskSource.storePinProtectedUserKey(
|
||||
userId = "mockId-1",
|
||||
pinProtectedUserKey = null,
|
||||
@@ -727,11 +734,15 @@ class VaultRepositoryTest {
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unlockVaultWithPin with VaultLockManager Success should unlock for the current user and return Success`() =
|
||||
fun `unlockVaultWithPin with a pin protected user key and SdkPinUnlock enabled should unlock with Pin and return Success`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
val mockVaultUnlockResult = VaultUnlockResult.Success
|
||||
prepareStateForUnlocking(unlockResult = mockVaultUnlockResult)
|
||||
prepareStateForUnlocking(
|
||||
unlockResult = mockVaultUnlockResult,
|
||||
persistentPinProtectedUserKeyEnvelope = null,
|
||||
ephemeralPinProtectedUserKeyEnvelope = null,
|
||||
)
|
||||
|
||||
val result = vaultRepository.unlockVaultWithPin(pin = "1234")
|
||||
|
||||
@@ -739,44 +750,7 @@ class VaultRepositoryTest {
|
||||
mockVaultUnlockResult,
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
email = "email",
|
||||
kdf = MOCK_PROFILE.toSdkParams(),
|
||||
initUserCryptoMethod = InitUserCryptoMethod.PinEnvelope(
|
||||
pin = "1234",
|
||||
pinProtectedUserKeyEnvelope = "mockKey-1",
|
||||
),
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unlockVaultWithPin with PinProtectedUserKeyEnvelope null and VaultLockManager Success should unlock with pin for the current user and return Success`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
val mockVaultUnlockResult = VaultUnlockResult.Success
|
||||
prepareStateForUnlocking(unlockResult = mockVaultUnlockResult)
|
||||
|
||||
fakeAuthDiskSource.storeEphemeralPinProtectedUserKeyEnvelope(
|
||||
userId = userId,
|
||||
pinProtectedUserKeyEnvelope = null,
|
||||
)
|
||||
fakeAuthDiskSource.storePersistentPinProtectedUserKeyEnvelope(
|
||||
userId = userId,
|
||||
pinProtectedUserKeyEnvelope = null,
|
||||
)
|
||||
|
||||
val result = vaultRepository.unlockVaultWithPin(pin = "1234")
|
||||
assertEquals(
|
||||
mockVaultUnlockResult,
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
coVerify(exactly = 1) {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
@@ -793,11 +767,16 @@ class VaultRepositoryTest {
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unlockVaultWithPin with VaultLockManager non-Success should unlock for the current user and return the error`() =
|
||||
fun `unlockVaultWithPin with a pin protected user key and SdkPinUnlock disabled should unlock with Pin and return Success`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
val mockVaultUnlockResult = VaultUnlockResult.InvalidStateError(error = null)
|
||||
prepareStateForUnlocking(unlockResult = mockVaultUnlockResult)
|
||||
val mockVaultUnlockResult = VaultUnlockResult.Success
|
||||
every { featureFlagManager.getFeatureFlag(FlagKey.SdkPinUnlock) } returns false
|
||||
prepareStateForUnlocking(
|
||||
unlockResult = mockVaultUnlockResult,
|
||||
persistentPinProtectedUserKeyEnvelope = null,
|
||||
ephemeralPinProtectedUserKeyEnvelope = null,
|
||||
)
|
||||
|
||||
val result = vaultRepository.unlockVaultWithPin(pin = "1234")
|
||||
|
||||
@@ -805,7 +784,163 @@ class VaultRepositoryTest {
|
||||
mockVaultUnlockResult,
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
coVerify(exactly = 1) {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
email = "email",
|
||||
kdf = MOCK_PROFILE.toSdkParams(),
|
||||
initUserCryptoMethod = InitUserCryptoMethod.Pin(
|
||||
pin = "1234",
|
||||
pinProtectedUserKey = "mockKey-1",
|
||||
),
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unlockVaultWithPin with SdkPinUnlock enabled and no pin protected user key should unlock with PinState and return Success`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
val mockVaultUnlockResult = VaultUnlockResult.Success
|
||||
prepareStateForUnlocking(
|
||||
unlockResult = mockVaultUnlockResult,
|
||||
pinProtectedUserKey = null,
|
||||
)
|
||||
|
||||
val result = vaultRepository.unlockVaultWithPin(pin = "1234")
|
||||
|
||||
assertEquals(
|
||||
mockVaultUnlockResult,
|
||||
result,
|
||||
)
|
||||
coVerify(exactly = 1) {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
email = "email",
|
||||
kdf = MOCK_PROFILE.toSdkParams(),
|
||||
initUserCryptoMethod = InitUserCryptoMethod.PinState(pin = "1234"),
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unlockVaultWithPin with SdkPinUnlock enabled and no pin keys stored should unlock with PinState and return Success`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
val mockVaultUnlockResult = VaultUnlockResult.Success
|
||||
prepareStateForUnlocking(
|
||||
unlockResult = mockVaultUnlockResult,
|
||||
pinProtectedUserKey = null,
|
||||
persistentPinProtectedUserKeyEnvelope = null,
|
||||
ephemeralPinProtectedUserKeyEnvelope = null,
|
||||
)
|
||||
|
||||
val result = vaultRepository.unlockVaultWithPin(pin = "1234")
|
||||
|
||||
assertEquals(
|
||||
mockVaultUnlockResult,
|
||||
result,
|
||||
)
|
||||
coVerify(exactly = 1) {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
email = "email",
|
||||
kdf = MOCK_PROFILE.toSdkParams(),
|
||||
initUserCryptoMethod = InitUserCryptoMethod.PinState(pin = "1234"),
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unlockVaultWithPin with SdkPinUnlock enabled and VaultLockManager non-Success should return the error`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
val mockVaultUnlockResult = VaultUnlockResult.InvalidStateError(error = null)
|
||||
prepareStateForUnlocking(
|
||||
unlockResult = mockVaultUnlockResult,
|
||||
pinProtectedUserKey = null,
|
||||
)
|
||||
|
||||
val result = vaultRepository.unlockVaultWithPin(pin = "1234")
|
||||
|
||||
assertEquals(
|
||||
mockVaultUnlockResult,
|
||||
result,
|
||||
)
|
||||
coVerify(exactly = 1) {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
email = "email",
|
||||
kdf = MOCK_PROFILE.toSdkParams(),
|
||||
initUserCryptoMethod = InitUserCryptoMethod.PinState(pin = "1234"),
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unlockVaultWithPin with SdkPinUnlock disabled and a pin protected user key envelope should unlock with PinEnvelope and return Success`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
val mockVaultUnlockResult = VaultUnlockResult.Success
|
||||
every { featureFlagManager.getFeatureFlag(FlagKey.SdkPinUnlock) } returns false
|
||||
prepareStateForUnlocking(unlockResult = mockVaultUnlockResult)
|
||||
fakeAuthDiskSource.storePinProtectedUserKey(
|
||||
userId = userId,
|
||||
pinProtectedUserKey = null,
|
||||
)
|
||||
|
||||
val result = vaultRepository.unlockVaultWithPin(pin = "1234")
|
||||
|
||||
assertEquals(
|
||||
mockVaultUnlockResult,
|
||||
result,
|
||||
)
|
||||
coVerify(exactly = 1) {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
email = "email",
|
||||
kdf = MOCK_PROFILE.toSdkParams(),
|
||||
initUserCryptoMethod = InitUserCryptoMethod.PinEnvelope(
|
||||
pin = "1234",
|
||||
pinProtectedUserKeyEnvelope = "mockKey-1",
|
||||
),
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unlockVaultWithPin with SdkPinUnlock disabled and VaultLockManager non-Success should return the error`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
val mockVaultUnlockResult = VaultUnlockResult.InvalidStateError(error = null)
|
||||
every { featureFlagManager.getFeatureFlag(FlagKey.SdkPinUnlock) } returns false
|
||||
prepareStateForUnlocking(
|
||||
unlockResult = mockVaultUnlockResult,
|
||||
pinProtectedUserKey = null,
|
||||
)
|
||||
|
||||
val result = vaultRepository.unlockVaultWithPin(pin = "1234")
|
||||
|
||||
assertEquals(
|
||||
mockVaultUnlockResult,
|
||||
result,
|
||||
)
|
||||
coVerify(exactly = 1) {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
@@ -1574,10 +1709,14 @@ class VaultRepositoryTest {
|
||||
/**
|
||||
* Prepares for an unlock call with the given [unlockResult].
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
private fun prepareStateForUnlocking(
|
||||
unlockResult: VaultUnlockResult,
|
||||
mockMasterPassword: String = "mockPassword-1",
|
||||
mockPin: String = "1234",
|
||||
pinProtectedUserKey: String? = "mockKey-1",
|
||||
persistentPinProtectedUserKeyEnvelope: String? = "mockKey-1",
|
||||
ephemeralPinProtectedUserKeyEnvelope: String? = "mockKey-1",
|
||||
) {
|
||||
val userId = "mockId-1"
|
||||
coEvery {
|
||||
@@ -1595,11 +1734,15 @@ class VaultRepositoryTest {
|
||||
)
|
||||
fakeAuthDiskSource.storePinProtectedUserKey(
|
||||
userId = userId,
|
||||
pinProtectedUserKey = "mockKey-1",
|
||||
pinProtectedUserKey = pinProtectedUserKey,
|
||||
)
|
||||
fakeAuthDiskSource.storeEphemeralPinProtectedUserKeyEnvelope(
|
||||
userId = userId,
|
||||
pinProtectedUserKeyEnvelope = ephemeralPinProtectedUserKeyEnvelope,
|
||||
)
|
||||
fakeAuthDiskSource.storePersistentPinProtectedUserKeyEnvelope(
|
||||
userId = userId,
|
||||
pinProtectedUserKeyEnvelope = "mockKey-1",
|
||||
pinProtectedUserKeyEnvelope = persistentPinProtectedUserKeyEnvelope,
|
||||
)
|
||||
fakeAuthDiskSource.storeOrganizationKeys(
|
||||
userId = userId,
|
||||
@@ -1627,31 +1770,62 @@ class VaultRepositoryTest {
|
||||
} returns unlockResult
|
||||
|
||||
// PIN unlock
|
||||
coEvery {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
email = "email",
|
||||
kdf = MOCK_PROFILE.toSdkParams(),
|
||||
initUserCryptoMethod = InitUserCryptoMethod.Pin(
|
||||
pin = mockPin,
|
||||
pinProtectedUserKey = "mockKey-1",
|
||||
),
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
)
|
||||
} returns unlockResult
|
||||
pinProtectedUserKey?.let {
|
||||
coEvery {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
email = "email",
|
||||
kdf = MOCK_PROFILE.toSdkParams(),
|
||||
initUserCryptoMethod = InitUserCryptoMethod.Pin(
|
||||
pin = mockPin,
|
||||
pinProtectedUserKey = it,
|
||||
),
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
)
|
||||
} returns unlockResult
|
||||
}
|
||||
|
||||
// PIN ENVELOPE unlock
|
||||
persistentPinProtectedUserKeyEnvelope?.let {
|
||||
coEvery {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
email = "email",
|
||||
kdf = MOCK_PROFILE.toSdkParams(),
|
||||
initUserCryptoMethod = InitUserCryptoMethod.PinEnvelope(
|
||||
pin = mockPin,
|
||||
pinProtectedUserKeyEnvelope = it,
|
||||
),
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
)
|
||||
} returns unlockResult
|
||||
}
|
||||
ephemeralPinProtectedUserKeyEnvelope?.let {
|
||||
coEvery {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
email = "email",
|
||||
kdf = MOCK_PROFILE.toSdkParams(),
|
||||
initUserCryptoMethod = InitUserCryptoMethod.PinEnvelope(
|
||||
pin = mockPin,
|
||||
pinProtectedUserKeyEnvelope = it,
|
||||
),
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
)
|
||||
} returns unlockResult
|
||||
}
|
||||
|
||||
// PIN STATE unlock
|
||||
coEvery {
|
||||
vaultLockManager.unlockVault(
|
||||
accountCryptographicState = MOCK_ACCOUNT_CRYPTOGRAPHIC_STATE,
|
||||
userId = userId,
|
||||
email = "email",
|
||||
kdf = MOCK_PROFILE.toSdkParams(),
|
||||
initUserCryptoMethod = InitUserCryptoMethod.PinEnvelope(
|
||||
pin = mockPin,
|
||||
pinProtectedUserKeyEnvelope = "mockKey-1",
|
||||
),
|
||||
initUserCryptoMethod = InitUserCryptoMethod.PinState(pin = mockPin),
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
)
|
||||
} returns unlockResult
|
||||
|
||||
@@ -50,6 +50,7 @@ sealed class FlagKey<out T : Any> {
|
||||
SendControls,
|
||||
SendControlsExistingSends,
|
||||
IdentityAutofill,
|
||||
SdkPinUnlock,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -228,6 +229,14 @@ sealed class FlagKey<out T : Any> {
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the feature flag key for gating PIN unlock through SDK.
|
||||
*/
|
||||
data object SdkPinUnlock : FlagKey<Boolean>() {
|
||||
override val keyName: String = "pm-31059-sdk-pin-unlock"
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
//region Dummy keys for testing
|
||||
/**
|
||||
* Data object holding the key for a [Boolean] flag to be used in tests.
|
||||
|
||||
@@ -80,6 +80,10 @@ class FlagKeyTest {
|
||||
FlagKey.IdentityAutofill.keyName,
|
||||
"pm-38138-mobile-identity-autofill",
|
||||
)
|
||||
assertEquals(
|
||||
FlagKey.SdkPinUnlock.keyName,
|
||||
"pm-31059-sdk-pin-unlock",
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -104,6 +108,7 @@ class FlagKeyTest {
|
||||
FlagKey.SendControls,
|
||||
FlagKey.SendControlsExistingSends,
|
||||
FlagKey.IdentityAutofill,
|
||||
FlagKey.SdkPinUnlock,
|
||||
).all {
|
||||
!it.defaultValue
|
||||
},
|
||||
|
||||
@@ -44,6 +44,7 @@ fun <T : Any> FlagKey<T>.ListItemContent(
|
||||
FlagKey.SendControls,
|
||||
FlagKey.SendControlsExistingSends,
|
||||
FlagKey.IdentityAutofill,
|
||||
FlagKey.SdkPinUnlock,
|
||||
-> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
BooleanFlagItem(
|
||||
@@ -115,4 +116,5 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
|
||||
}
|
||||
|
||||
FlagKey.IdentityAutofill -> stringResource(BitwardenString.identity_autofill)
|
||||
FlagKey.SdkPinUnlock -> stringResource(BitwardenString.sdk_pin_unlock)
|
||||
}
|
||||
@@ -61,6 +61,7 @@
|
||||
<string name="send_controls">Send Controls</string>
|
||||
<string name="send_controls_existing_sends">Send Controls - Existing Sends</string>
|
||||
<string name="identity_autofill">Identity Autofill</string>
|
||||
<string name="sdk_pin_unlock">SDK PIN Unlock</string>
|
||||
|
||||
<!-- endregion Debug Menu -->
|
||||
</resources>
|
||||
Reference in new issue
Block a user