BIT-2438: Update push notification processing logic to be more lenient (#3393)

This commit is contained in:
David Perez 2024-07-02 14:53:29 -05:00 committed by GitHub
parent f5039d72b9
commit b181d0d026
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 114 additions and 93 deletions

View file

@ -120,7 +120,7 @@ class PushManagerImpl @Inject constructor(
onMessageReceived(notification)
}
@Suppress("LongMethod", "CyclomaticComplexMethod", "ReturnCount")
@Suppress("LongMethod", "CyclomaticComplexMethod")
private fun onMessageReceived(notification: BitwardenNotification) {
if (authDiskSource.uniqueAppId == notification.contextId) return
@ -130,62 +130,67 @@ class PushManagerImpl @Inject constructor(
NotificationType.AUTH_REQUEST,
NotificationType.AUTH_REQUEST_RESPONSE,
-> {
val payload: NotificationPayload.PasswordlessRequestNotification =
json.decodeFromString(string = notification.payload)
mutablePasswordlessRequestSharedFlow.tryEmit(
PasswordlessRequestData(
loginRequestId = payload.id,
userId = payload.userId,
),
)
json
.decodeFromString<NotificationPayload.PasswordlessRequestNotification>(
string = notification.payload,
)
.takeIf { it.loginRequestId != null && it.userId != null }
?.let {
mutablePasswordlessRequestSharedFlow.tryEmit(
PasswordlessRequestData(
loginRequestId = requireNotNull(it.loginRequestId),
userId = requireNotNull(it.userId),
),
)
}
}
NotificationType.LOG_OUT -> {
val payload: NotificationPayload.UserNotification =
json.decodeFromString(notification.payload)
mutableLogoutSharedFlow.tryEmit(
NotificationLogoutData(payload.userId),
)
json
.decodeFromString<NotificationPayload.UserNotification>(
string = notification.payload,
)
.userId
?.let { mutableLogoutSharedFlow.tryEmit(NotificationLogoutData(it)) }
}
NotificationType.SYNC_CIPHER_CREATE,
NotificationType.SYNC_CIPHER_UPDATE,
-> {
val payload: NotificationPayload.SyncCipherNotification =
json.decodeFromString(notification.payload)
@Suppress("ComplexCondition")
if (payload.id == null ||
payload.revisionDate == null ||
!isLoggedIn(userId) ||
!payload.userMatchesNotification(userId)
) {
return
}
mutableSyncCipherUpsertSharedFlow.tryEmit(
SyncCipherUpsertData(
cipherId = payload.id,
revisionDate = payload.revisionDate,
organizationId = payload.organizationId,
collectionIds = payload.collectionIds,
isUpdate = type == NotificationType.SYNC_CIPHER_UPDATE,
),
)
json
.decodeFromString<NotificationPayload.SyncCipherNotification>(
string = notification.payload,
)
.takeIf { isLoggedIn(userId) && it.userMatchesNotification(userId) }
?.takeIf {
it.cipherId != null &&
it.revisionDate != null &&
it.organizationId != null &&
it.collectionIds != null
}
?.let {
mutableSyncCipherUpsertSharedFlow.tryEmit(
SyncCipherUpsertData(
cipherId = requireNotNull(it.cipherId),
revisionDate = requireNotNull(it.revisionDate),
organizationId = requireNotNull(it.organizationId),
collectionIds = requireNotNull(it.collectionIds),
isUpdate = type == NotificationType.SYNC_CIPHER_UPDATE,
),
)
}
}
NotificationType.SYNC_CIPHER_DELETE,
NotificationType.SYNC_LOGIN_DELETE,
-> {
val payload: NotificationPayload.SyncCipherNotification =
json.decodeFromString(notification.payload)
if (payload.id == null ||
!isLoggedIn(userId) ||
!payload.userMatchesNotification(userId)
) {
return
}
mutableSyncCipherDeleteSharedFlow.tryEmit(
SyncCipherDeleteData(payload.id),
)
json
.decodeFromString<NotificationPayload.SyncCipherNotification>(
string = notification.payload,
)
.takeIf { isLoggedIn(userId) && it.userMatchesNotification(userId) }
?.cipherId
?.let { mutableSyncCipherDeleteSharedFlow.tryEmit(SyncCipherDeleteData(it)) }
}
NotificationType.SYNC_CIPHERS,
@ -198,55 +203,67 @@ class PushManagerImpl @Inject constructor(
NotificationType.SYNC_FOLDER_CREATE,
NotificationType.SYNC_FOLDER_UPDATE,
-> {
val payload: NotificationPayload.SyncFolderNotification =
json.decodeFromString(notification.payload)
if (!isLoggedIn(userId) || !payload.userMatchesNotification(userId)) return
mutableSyncFolderUpsertSharedFlow.tryEmit(
SyncFolderUpsertData(
folderId = payload.id,
revisionDate = payload.revisionDate,
isUpdate = type == NotificationType.SYNC_FOLDER_UPDATE,
),
)
json
.decodeFromString<NotificationPayload.SyncFolderNotification>(
string = notification.payload,
)
.takeIf { isLoggedIn(userId) && it.userMatchesNotification(userId) }
?.takeIf { it.folderId != null && it.revisionDate != null }
?.let {
mutableSyncFolderUpsertSharedFlow.tryEmit(
SyncFolderUpsertData(
folderId = requireNotNull(it.folderId),
revisionDate = requireNotNull(it.revisionDate),
isUpdate = type == NotificationType.SYNC_FOLDER_UPDATE,
),
)
}
}
NotificationType.SYNC_FOLDER_DELETE -> {
val payload: NotificationPayload.SyncFolderNotification =
json.decodeFromString(notification.payload)
if (!isLoggedIn(userId) || !payload.userMatchesNotification(userId)) return
mutableSyncFolderDeleteSharedFlow.tryEmit(
SyncFolderDeleteData(payload.id),
)
json
.decodeFromString<NotificationPayload.SyncFolderNotification>(
string = notification.payload,
)
.takeIf { isLoggedIn(userId) && it.userMatchesNotification(userId) }
?.folderId
?.let { mutableSyncFolderDeleteSharedFlow.tryEmit(SyncFolderDeleteData(it)) }
}
NotificationType.SYNC_ORG_KEYS -> {
if (!isLoggedIn(userId)) return
mutableSyncOrgKeysSharedFlow.tryEmit(Unit)
if (isLoggedIn(userId)) {
mutableSyncOrgKeysSharedFlow.tryEmit(Unit)
}
}
NotificationType.SYNC_SEND_CREATE,
NotificationType.SYNC_SEND_UPDATE,
-> {
val payload: NotificationPayload.SyncSendNotification =
json.decodeFromString(notification.payload)
if (!isLoggedIn(userId) || !payload.userMatchesNotification(userId)) return
mutableSyncSendUpsertSharedFlow.tryEmit(
SyncSendUpsertData(
sendId = payload.id,
revisionDate = payload.revisionDate,
isUpdate = type == NotificationType.SYNC_SEND_UPDATE,
),
)
json
.decodeFromString<NotificationPayload.SyncSendNotification>(
string = notification.payload,
)
.takeIf { isLoggedIn(userId) && it.userMatchesNotification(userId) }
?.takeIf { it.sendId != null && it.revisionDate != null }
?.let {
mutableSyncSendUpsertSharedFlow.tryEmit(
SyncSendUpsertData(
sendId = requireNotNull(it.sendId),
revisionDate = requireNotNull(it.revisionDate),
isUpdate = type == NotificationType.SYNC_SEND_UPDATE,
),
)
}
}
NotificationType.SYNC_SEND_DELETE -> {
val payload: NotificationPayload.SyncSendNotification =
json.decodeFromString(notification.payload)
if (!isLoggedIn(userId) || !payload.userMatchesNotification(userId)) return
mutableSyncSendDeleteSharedFlow.tryEmit(
SyncSendDeleteData(payload.id),
)
json
.decodeFromString<NotificationPayload.SyncSendNotification>(
string = notification.payload,
)
.takeIf { isLoggedIn(userId) && it.userMatchesNotification(userId) }
?.sendId
?.let { mutableSyncSendDeleteSharedFlow.tryEmit(SyncSendDeleteData(it)) }
}
}
}
@ -290,15 +307,15 @@ class PushManagerImpl @Inject constructor(
if (token == currentToken) {
// Our token is up-to-date, so just update the last registration date
pushDiskSource.storeLastPushTokenRegistrationDate(
userId,
ZonedDateTime.ofInstant(clock.instant(), ZoneOffset.UTC),
userId = userId,
registrationDate = ZonedDateTime.ofInstant(clock.instant(), ZoneOffset.UTC),
)
return
}
pushService
.putDeviceToken(
PushTokenRequest(token),
body = PushTokenRequest(token),
)
.fold(
onSuccess = {

View file

@ -1,5 +1,6 @@
package com.x8bit.bitwarden.data.platform.manager.model
import com.x8bit.bitwarden.data.platform.manager.PushManager
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@ -7,6 +8,9 @@ import java.time.ZonedDateTime
/**
* The payload of a push notification.
*
* Note: The data we receive is not always reliable, so everything is nullable and we validate the
* data in the [PushManager] as necessary.
*/
@Serializable
sealed class NotificationPayload {
@ -20,7 +24,7 @@ sealed class NotificationPayload {
*/
@Serializable
data class SyncCipherNotification(
@SerialName("Id") val id: String?,
@SerialName("Id") val cipherId: String?,
@SerialName("UserId") override val userId: String?,
@SerialName("OrganizationId") val organizationId: String?,
@SerialName("CollectionIds") val collectionIds: List<String>?,
@ -33,10 +37,10 @@ sealed class NotificationPayload {
*/
@Serializable
data class SyncFolderNotification(
@SerialName("Id") val id: String,
@SerialName("UserId") override val userId: String,
@SerialName("Id") val folderId: String?,
@SerialName("UserId") override val userId: String?,
@Contextual
@SerialName("RevisionDate") val revisionDate: ZonedDateTime,
@SerialName("RevisionDate") val revisionDate: ZonedDateTime?,
) : NotificationPayload()
/**
@ -44,9 +48,9 @@ sealed class NotificationPayload {
*/
@Serializable
data class UserNotification(
@SerialName("UserId") override val userId: String,
@SerialName("UserId") override val userId: String?,
@Contextual
@SerialName("Date") val date: ZonedDateTime,
@SerialName("Date") val date: ZonedDateTime?,
) : NotificationPayload()
/**
@ -54,10 +58,10 @@ sealed class NotificationPayload {
*/
@Serializable
data class SyncSendNotification(
@SerialName("Id") val id: String,
@SerialName("UserId") override val userId: String,
@SerialName("Id") val sendId: String?,
@SerialName("UserId") override val userId: String?,
@Contextual
@SerialName("RevisionDate") val revisionDate: ZonedDateTime,
@SerialName("RevisionDate") val revisionDate: ZonedDateTime?,
) : NotificationPayload()
/**
@ -65,7 +69,7 @@ sealed class NotificationPayload {
*/
@Serializable
data class PasswordlessRequestNotification(
@SerialName("UserId") override val userId: String,
@SerialName("Id") val id: String,
@SerialName("UserId") override val userId: String?,
@SerialName("Id") val loginRequestId: String?,
) : NotificationPayload()
}