PM-10600: Re-register device with push token every 7 days.

The Azure Notification Hub tags can change frequently, like here for Notification Center work we need to add new tags to existing devices registrations. Instead of painful migration plan, this PR adds a frequent re-registration of push notification for the device against Bitwarden Server.
This commit is contained in:
Maciej Zieniuk 2024-11-14 16:34:33 +00:00
parent 911c9e4704
commit 5218dd1693
No known key found for this signature in database
GPG key ID: 9CACE59F1272ACD9

View file

@ -20,18 +20,34 @@ import com.x8bit.bitwarden.data.platform.manager.model.SyncSendUpsertData
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.data.platform.util.decodeFromStringOrNull import com.x8bit.bitwarden.data.platform.util.decodeFromStringOrNull
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.time.Clock import java.time.Clock
import java.time.ZoneOffset import java.time.ZoneOffset
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import javax.inject.Inject import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.minutes
import kotlin.time.toJavaDuration
/**
* The amount of time to delay before a subsequent attempts to expire the push token.
*/
private val PUSH_TOKEN_EXPIRE_INTERVAL: Duration = 1.minutes
/**
* The amount of time to delay before expiring the push token.
*/
private val PUSH_TOKEN_EXPIRE_DELAY: Duration = 7.days
/** /**
* Primary implementation of [PushManager]. * Primary implementation of [PushManager].
@ -98,6 +114,8 @@ class PushManagerImpl @Inject constructor(
private val activeUserId: String? private val activeUserId: String?
get() = authDiskSource.userState?.activeUserId get() = authDiskSource.userState?.activeUserId
private var registerPushTokenJob: Job = Job().apply { complete() }
init { init {
authDiskSource authDiskSource
.activeUserIdChangesFlow .activeUserIdChangesFlow
@ -279,10 +297,14 @@ class PushManagerImpl @Inject constructor(
val userId = activeUserId ?: return val userId = activeUserId ?: return
if (!isLoggedIn(userId)) return if (!isLoggedIn(userId)) return
// If the last registered token is from less than a day before, skip this for now // Periodically check if the token needs to be re-registered with Bitwarden Server
val lastRegistration = pushDiskSource.getLastPushTokenRegistrationDate(userId)?.toInstant() registerPushTokenJob.cancel()
val dayBefore = clock.instant().minus(1, ChronoUnit.DAYS) registerPushTokenJob = ioScope.launch {
if (lastRegistration?.isAfter(dayBefore) == true) return while (coroutineContext.isActive) {
delay(duration = PUSH_TOKEN_EXPIRE_INTERVAL)
registerStoredPushTokenIfNecessaryInternal(userId)
}
}
ioScope.launch { ioScope.launch {
pushDiskSource.registeredPushToken?.let { pushDiskSource.registeredPushToken?.let {
@ -296,19 +318,30 @@ class PushManagerImpl @Inject constructor(
private suspend fun registerPushTokenIfNecessaryInternal(userId: String, token: String) { private suspend fun registerPushTokenIfNecessaryInternal(userId: String, token: String) {
val currentToken = pushDiskSource.getCurrentPushToken(userId) val currentToken = pushDiskSource.getCurrentPushToken(userId)
if (token == currentToken) { if (token == currentToken) {
// Our token is up-to-date, so just update the last registration date
pushDiskSource.storeLastPushTokenRegistrationDate(
userId = userId,
registrationDate = ZonedDateTime.ofInstant(clock.instant(), ZoneOffset.UTC),
)
return return
} }
registerPushTokenInternal(userId, token)
}
private suspend fun registerStoredPushTokenIfNecessaryInternal(userId: String) {
if (!isLoggedIn(userId)) return
val lastRegistration =
pushDiskSource.getLastPushTokenRegistrationDate(userId)?.toInstant() ?: return
val expiryTime = clock.instant().minus(PUSH_TOKEN_EXPIRE_DELAY.toJavaDuration())
if (lastRegistration.isAfter(expiryTime)) return
val currentToken = pushDiskSource.getCurrentPushToken(userId) ?: return
registerPushTokenInternal(userId, currentToken)
}
private suspend fun registerPushTokenInternal(userId: String, newToken: String) {
pushService pushService
.putDeviceToken( .putDeviceToken(
body = PushTokenRequest(token), body = PushTokenRequest(newToken),
) )
.fold( .fold(
onSuccess = { onSuccess = {
@ -318,7 +351,7 @@ class PushManagerImpl @Inject constructor(
) )
pushDiskSource.storeCurrentPushToken( pushDiskSource.storeCurrentPushToken(
userId = userId, userId = userId,
pushToken = token, pushToken = newToken,
) )
}, },
onFailure = { onFailure = {