BITAU-69 Check for OS version in AuthenticatorBridgeManager (#4019)

This commit is contained in:
Andrew Haisting 2024-10-03 14:06:15 -05:00 committed by GitHub
parent 20383f06a8
commit 32f2bfb29f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 58 additions and 11 deletions

View file

@ -5,6 +5,7 @@ import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageManager.NameNotFoundException
import android.os.Build
import android.os.IBinder
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
@ -18,6 +19,7 @@ import com.bitwarden.authenticatorbridge.provider.AuthenticatorBridgeCallbackPro
import com.bitwarden.authenticatorbridge.provider.StubAuthenticatorBridgeCallbackProvider
import com.bitwarden.authenticatorbridge.provider.SymmetricKeyStorageProvider
import com.bitwarden.authenticatorbridge.util.decrypt
import com.bitwarden.authenticatorbridge.util.isBuildVersionBelow
import com.bitwarden.authenticatorbridge.util.toFingerprint
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -58,10 +60,10 @@ internal class AuthenticatorBridgeManagerImpl(
*/
private val mutableSharedAccountsStateFlow: MutableStateFlow<AccountSyncState> =
MutableStateFlow(
if (isBitwardenAppInstalled()) {
AccountSyncState.Loading
} else {
AccountSyncState.AppNotInstalled
when {
isBuildVersionBelow(Build.VERSION_CODES.S) -> AccountSyncState.OsVersionNotSupported
!isBitwardenAppInstalled() -> AccountSyncState.AppNotInstalled
else -> AccountSyncState.Loading
}
)
@ -102,6 +104,10 @@ internal class AuthenticatorBridgeManagerImpl(
}
private fun bindService() {
if (isBuildVersionBelow(Build.VERSION_CODES.S)) {
mutableSharedAccountsStateFlow.value = AccountSyncState.OsVersionNotSupported
return
}
if (!isBitwardenAppInstalled()) {
mutableSharedAccountsStateFlow.value = AccountSyncState.AppNotInstalled
return

View file

@ -27,6 +27,11 @@ sealed class AccountSyncState {
*/
data object Loading : AccountSyncState()
/**
* OS version can't support account syncing.
*/
data object OsVersionNotSupported: AccountSyncState()
/**
* Accounts successfully synced.
*/

View file

@ -0,0 +1,10 @@
package com.bitwarden.authenticatorbridge.util
import android.os.Build
/**
* Returns true if the current OS build version is below the provided [version].
*
* @see Build.VERSION_CODES
*/
fun isBuildVersionBelow(version: Int): Boolean = version > Build.VERSION.SDK_INT

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageManager.NameNotFoundException
import android.os.Build
import com.bitwarden.authenticatorbridge.IAuthenticatorBridgeService
import com.bitwarden.authenticatorbridge.IAuthenticatorBridgeServiceCallback
import com.bitwarden.authenticatorbridge.manager.model.AccountSyncState
@ -15,6 +16,7 @@ import com.bitwarden.authenticatorbridge.util.FakeSymmetricKeyStorageProvider
import com.bitwarden.authenticatorbridge.util.TestAuthenticatorBridgeCallbackProvider
import com.bitwarden.authenticatorbridge.util.decrypt
import com.bitwarden.authenticatorbridge.util.generateSecretKey
import com.bitwarden.authenticatorbridge.util.isBuildVersionBelow
import com.bitwarden.authenticatorbridge.util.toFingerprint
import com.bitwarden.authenticatorbridge.util.toSymmetricEncryptionKeyData
import io.mockk.every
@ -45,23 +47,27 @@ class AuthenticatorBridgeManagerTest {
private val fakeSymmetricKeyStorageProvider = FakeSymmetricKeyStorageProvider()
private val testAuthenticatorBridgeCallbackProvider = TestAuthenticatorBridgeCallbackProvider()
private val manager: AuthenticatorBridgeManagerImpl = AuthenticatorBridgeManagerImpl(
context = context,
connectionType = AuthenticatorBridgeConnectionType.DEV,
symmetricKeyStorageProvider = fakeSymmetricKeyStorageProvider,
callbackProvider = testAuthenticatorBridgeCallbackProvider,
processLifecycleOwner = fakeLifecycleOwner,
)
private lateinit var manager: AuthenticatorBridgeManagerImpl
@BeforeEach
fun setup() {
mockkStatic(::isBuildVersionBelow)
every { isBuildVersionBelow(Build.VERSION_CODES.S) } returns false
mockkConstructor(Intent::class)
mockkStatic(IAuthenticatorBridgeService.Stub::class)
mockkStatic(EncryptedSharedAccountData::decrypt)
manager = AuthenticatorBridgeManagerImpl(
context = context,
connectionType = AuthenticatorBridgeConnectionType.DEV,
symmetricKeyStorageProvider = fakeSymmetricKeyStorageProvider,
callbackProvider = testAuthenticatorBridgeCallbackProvider,
processLifecycleOwner = fakeLifecycleOwner,
)
}
@AfterEach
fun teardown() {
mockkStatic(::isBuildVersionBelow)
unmockkConstructor(Intent::class)
unmockkStatic(IAuthenticatorBridgeService.Stub::class)
unmockkStatic(EncryptedSharedAccountData::decrypt)
@ -87,6 +93,26 @@ class AuthenticatorBridgeManagerTest {
assertEquals(AccountSyncState.AppNotInstalled, manager.accountSyncStateFlow.value)
}
@Test
fun `initial AccountSyncState should be OsVersionNotSupported when OS level is below S`() {
every { isBuildVersionBelow(Build.VERSION_CODES.S) } returns true
val manager = AuthenticatorBridgeManagerImpl(
context = context,
connectionType = AuthenticatorBridgeConnectionType.DEV,
symmetricKeyStorageProvider = fakeSymmetricKeyStorageProvider,
callbackProvider = testAuthenticatorBridgeCallbackProvider,
processLifecycleOwner = fakeLifecycleOwner,
)
assertEquals(AccountSyncState.OsVersionNotSupported, manager.accountSyncStateFlow.value)
}
@Test
fun `onStart when OS level is below S should set state to OsVersionNotSupported`() {
every { isBuildVersionBelow(Build.VERSION_CODES.S) } returns true
fakeLifecycleOwner.lifecycle.dispatchOnStart()
assertEquals(AccountSyncState.OsVersionNotSupported, manager.accountSyncStateFlow.value)
}
@Test
fun `onStart when bindService fails should set state to error`() {
val mockIntent: Intent = mockk()