mirror of
https://github.com/bitwarden/android.git
synced 2025-12-11 04:39:19 -06:00
[PM-26736] Prevent logout notification on KDF change (#6038)
This commit is contained in:
parent
714f7cfadc
commit
a70b2172cb
@ -1,5 +1,6 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.core.data.util.decodeFromStringOrNull
|
||||
import com.bitwarden.data.manager.DispatcherManager
|
||||
@ -13,6 +14,7 @@ import com.x8bit.bitwarden.data.platform.manager.model.NotificationLogoutData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.NotificationPayload
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.NotificationType
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.PasswordlessRequestData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.PushNotificationLogOutReason
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncCipherDeleteData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncCipherUpsertData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncFolderDeleteData
|
||||
@ -44,12 +46,14 @@ private val PUSH_TOKEN_UPDATE_DELAY: Duration = 7.days
|
||||
/**
|
||||
* Primary implementation of [PushManager].
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
class PushManagerImpl @Inject constructor(
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
private val pushDiskSource: PushDiskSource,
|
||||
private val pushService: PushService,
|
||||
private val clock: Clock,
|
||||
private val json: Json,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
dispatcherManager: DispatcherManager,
|
||||
) : PushManager {
|
||||
private val ioScope = CoroutineScope(dispatcherManager.io)
|
||||
@ -157,8 +161,15 @@ class PushManagerImpl @Inject constructor(
|
||||
.decodeFromString<NotificationPayload.UserNotification>(
|
||||
string = notification.payload,
|
||||
)
|
||||
.userId
|
||||
?.let { mutableLogoutSharedFlow.tryEmit(NotificationLogoutData(it)) }
|
||||
.takeUnless {
|
||||
featureFlagManager.getFeatureFlag(FlagKey.NoLogoutOnKdfChange) &&
|
||||
it.pushNotificationLogOutReason ==
|
||||
PushNotificationLogOutReason.KDF_CHANGE
|
||||
}
|
||||
?.userId
|
||||
?.let {
|
||||
mutableLogoutSharedFlow.tryEmit(NotificationLogoutData(userId = it))
|
||||
}
|
||||
}
|
||||
|
||||
NotificationType.SYNC_CIPHER_CREATE,
|
||||
|
||||
@ -302,6 +302,7 @@ object PlatformManagerModule {
|
||||
dispatcherManager: DispatcherManager,
|
||||
clock: Clock,
|
||||
json: Json,
|
||||
featureFlagManager: FeatureFlagManager,
|
||||
): PushManager = PushManagerImpl(
|
||||
authDiskSource = authDiskSource,
|
||||
pushDiskSource = pushDiskSource,
|
||||
@ -309,6 +310,7 @@ object PlatformManagerModule {
|
||||
dispatcherManager = dispatcherManager,
|
||||
clock = clock,
|
||||
json = json,
|
||||
featureFlagManager = featureFlagManager,
|
||||
)
|
||||
|
||||
@Provides
|
||||
|
||||
@ -50,9 +50,15 @@ sealed class NotificationPayload {
|
||||
*/
|
||||
@Serializable
|
||||
data class UserNotification(
|
||||
@JsonNames("UserId", "userId") override val userId: String?,
|
||||
@JsonNames("UserId", "userId")
|
||||
override val userId: String?,
|
||||
|
||||
@Contextual
|
||||
@JsonNames("Date", "date") val date: ZonedDateTime?,
|
||||
@JsonNames("Date", "date")
|
||||
val date: ZonedDateTime?,
|
||||
|
||||
@JsonNames("PushNotificationLogOutReason", "pushNotificationLogOutReason")
|
||||
val pushNotificationLogOutReason: PushNotificationLogOutReason?,
|
||||
) : NotificationPayload()
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
|
||||
/**
|
||||
* Enumerated values to represent the possible reasons for a log out push notification
|
||||
*/
|
||||
enum class PushNotificationLogOutReason {
|
||||
@SerialName("0")
|
||||
KDF_CHANGE,
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.core.data.util.asFailure
|
||||
import com.bitwarden.core.data.util.asSuccess
|
||||
import com.bitwarden.core.di.CoreModule
|
||||
@ -26,6 +27,7 @@ import com.x8bit.bitwarden.data.platform.manager.model.SyncSendDeleteData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncSendUpsertData
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
@ -55,6 +57,10 @@ class PushManagerTest {
|
||||
coEvery { putDeviceToken(any()) } returns Unit.asSuccess()
|
||||
}
|
||||
|
||||
private val mockFeatureFlagManager = mockk<FeatureFlagManager>(relaxed = true) {
|
||||
every { getFeatureFlag(FlagKey.NoLogoutOnKdfChange) } returns false
|
||||
}
|
||||
|
||||
private lateinit var pushManager: PushManager
|
||||
|
||||
@BeforeEach
|
||||
@ -66,6 +72,7 @@ class PushManagerTest {
|
||||
dispatcherManager = dispatcherManager,
|
||||
clock = clock,
|
||||
json = CoreModule.providesJson(),
|
||||
featureFlagManager = mockFeatureFlagManager,
|
||||
)
|
||||
}
|
||||
|
||||
@ -135,6 +142,28 @@ class PushManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `onMessageReceived with logout with kdf change as reason should not emit to logoutFlow`() =
|
||||
runTest {
|
||||
every {
|
||||
mockFeatureFlagManager.getFeatureFlag(FlagKey.NoLogoutOnKdfChange)
|
||||
} returns true
|
||||
|
||||
val accountTokens = AccountTokensJson(
|
||||
accessToken = "accessToken",
|
||||
refreshToken = "refreshToken",
|
||||
)
|
||||
authDiskSource.storeAccountTokens(userId, accountTokens)
|
||||
authDiskSource.userState =
|
||||
UserStateJson(userId, mapOf(userId to mockk<AccountJson>()))
|
||||
|
||||
pushManager.logoutFlow.test {
|
||||
pushManager.onMessageReceived(LOGOUT_KDF_NOTIFICATION_MAP)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class LoggedOutUserState {
|
||||
@BeforeEach
|
||||
@ -156,6 +185,18 @@ class PushManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with logout with KDF reason do not emits to logoutFlow`() =
|
||||
runTest {
|
||||
every {
|
||||
mockFeatureFlagManager.getFeatureFlag(FlagKey.NoLogoutOnKdfChange)
|
||||
} returns true
|
||||
pushManager.logoutFlow.test {
|
||||
pushManager.onMessageReceived(LOGOUT_KDF_NOTIFICATION_MAP)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with ciphers emits to fullSyncFlow`() = runTest {
|
||||
pushManager.fullSyncFlow.test {
|
||||
@ -517,6 +558,14 @@ class PushManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with logout with kdf reason does nothing`() = runTest {
|
||||
pushManager.logoutFlow.test {
|
||||
pushManager.onMessageReceived(LOGOUT_KDF_NOTIFICATION_MAP)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with sync ciphers does nothing`() = runTest {
|
||||
pushManager.fullSyncFlow.test {
|
||||
@ -575,6 +624,18 @@ class PushManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with logout with kdf reason does not emit to logoutFlow`() =
|
||||
runTest {
|
||||
every {
|
||||
mockFeatureFlagManager.getFeatureFlag(FlagKey.NoLogoutOnKdfChange)
|
||||
} returns true
|
||||
pushManager.logoutFlow.test {
|
||||
pushManager.onMessageReceived(LOGOUT_KDF_NOTIFICATION_MAP)
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessageReceived with sync ciphers emits to fullSyncFlow`() = runTest {
|
||||
pushManager.fullSyncFlow.test {
|
||||
@ -908,6 +969,16 @@ private val LOGOUT_NOTIFICATION_MAP = mapOf(
|
||||
}""",
|
||||
)
|
||||
|
||||
private val LOGOUT_KDF_NOTIFICATION_MAP = mapOf(
|
||||
"contextId" to "801f459d-8e51-47d0-b072-3f18c9f66f64",
|
||||
"type" to "11",
|
||||
"payload" to """{
|
||||
"UserId": "078966a2-93c2-4618-ae2a-0a2394c88d37",
|
||||
"Date": "2023-10-27T12:00:00.000Z",
|
||||
"PushNotificationLogOutReason": "0"
|
||||
}""",
|
||||
)
|
||||
|
||||
private val SYNC_CIPHER_CREATE_NOTIFICATION_MAP = mapOf(
|
||||
"contextId" to "801f459d-8e51-47d0-b072-3f18c9f66f64",
|
||||
"type" to "1",
|
||||
|
||||
@ -34,6 +34,7 @@ sealed class FlagKey<out T : Any> {
|
||||
CredentialExchangeProtocolExport,
|
||||
ForceUpdateKdfSettings,
|
||||
CipherKeyEncryption,
|
||||
NoLogoutOnKdfChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -80,6 +81,14 @@ sealed class FlagKey<out T : Any> {
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the feature flag key for the No Logout On KDF Change feature.
|
||||
*/
|
||||
data object NoLogoutOnKdfChange : FlagKey<Boolean>() {
|
||||
override val keyName: String = "pm-23995-no-logout-on-kdf-change"
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
//region Dummy keys for testing
|
||||
/**
|
||||
* Data object holding the key for a [Boolean] flag to be used in tests.
|
||||
|
||||
@ -28,6 +28,7 @@ fun <T : Any> FlagKey<T>.ListItemContent(
|
||||
FlagKey.CredentialExchangeProtocolExport,
|
||||
FlagKey.CipherKeyEncryption,
|
||||
FlagKey.ForceUpdateKdfSettings,
|
||||
FlagKey.NoLogoutOnKdfChange,
|
||||
-> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
BooleanFlagItem(
|
||||
@ -73,6 +74,7 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
|
||||
FlagKey.CredentialExchangeProtocolExport -> stringResource(BitwardenString.cxp_export)
|
||||
FlagKey.CipherKeyEncryption -> stringResource(BitwardenString.cipher_key_encryption)
|
||||
FlagKey.ForceUpdateKdfSettings -> stringResource(BitwardenString.force_update_kdf_settings)
|
||||
FlagKey.NoLogoutOnKdfChange -> stringResource(BitwardenString.avoid_logout_on_kdf_change)
|
||||
FlagKey.BitwardenAuthenticationEnabled -> {
|
||||
stringResource(BitwardenString.bitwarden_authentication_enabled)
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
<string name="import_format_label_lastpass_json">LastPass (.json)</string>
|
||||
<string name="import_format_label_aegis_json">Aegis (.json)</string>
|
||||
<string name="force_update_kdf_settings">Force update KDF settings</string>
|
||||
<string name="avoid_logout_on_kdf_change">Avoid logout on KDF change</string>
|
||||
|
||||
<!-- endregion Debug Menu -->
|
||||
</resources>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user