From 38051644892eba8adffbd0d76f240272fe409122 Mon Sep 17 00:00:00 2001 From: Artem Chepurnoy Date: Tue, 23 Jan 2024 21:01:13 +0200 Subject: [PATCH] Calculate password strength during the initial sync of the ciphers --- .../keyguard/common/io/IOException.kt | 2 +- .../keyguard/feature/home/HomeScreen.kt | 50 ------------------- .../provider/bitwarden/api/SyncEngine.kt | 8 +-- .../keyguard/provider/bitwarden/api/fff.kt | 40 ++++++++++++--- .../usecase/internal/SyncByTokenImpl.kt | 4 ++ 5 files changed, 44 insertions(+), 60 deletions(-) diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/io/IOException.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/io/IOException.kt index 2aa88e30..17041296 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/io/IOException.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/io/IOException.kt @@ -80,7 +80,7 @@ inline fun IO.handleErrorWith( inline fun IO.handleError( crossinline predicate: (Throwable) -> Boolean = { true }, - crossinline block: (Throwable) -> T, + crossinline block: suspend (Throwable) -> T, ): IO = ioEffect { try { invoke() diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/HomeScreen.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/HomeScreen.kt index 19d30357..8244d3dd 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/HomeScreen.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/HomeScreen.kt @@ -143,61 +143,11 @@ private const val ROUTE_NAME = "home" private val vaultRoute = VaultRoute() -@Composable -fun HomeEffect() { - val db by rememberInstance() - val ps by rememberInstance() - LaunchedEffect(Unit) { - db.mutate { - val ciphers = it.cipherQueries.get().executeAsList() - val www = ciphers - .filter { it.data_.login?.password != null && it.data_.login.passwordStrength == null } - .mapNotNull { - val password = it.data_.login?.password!! - val s = ps(password) - .attempt() - .bind() - .getOrNull() - if (s != null) { - val login = it.data_.login.copy( - passwordStrength = BitwardenCipher.Login.PasswordStrength( - password = password, - crackTimeSeconds = s.crackTimeSeconds, - version = 1, - ), - ) - it.copy( - data_ = it.data_.copy( - login = login, - ), - ) - } else { - null - } - } - .toTypedArray() - recordLog("Replaced ${www.size} entities") - it.transaction { - www.forEach { cipher -> - it.cipherQueries.insert( - cipherId = cipher.cipherId, - accountId = cipher.accountId, - folderId = cipher.folderId, - data = cipher.data_, - ) - } - } - }.attempt().bind() - } -} - @Composable fun HomeScreen( defaultRoute: Route = vaultRoute, navBarVisible: Boolean = true, ) { - HomeEffect() - val navRoutes = remember { listOf( Rail( diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/provider/bitwarden/api/SyncEngine.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/provider/bitwarden/api/SyncEngine.kt index 2b1274b2..b87a6856 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/provider/bitwarden/api/SyncEngine.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/provider/bitwarden/api/SyncEngine.kt @@ -6,6 +6,7 @@ import com.artemchep.keyguard.common.service.crypto.CipherEncryptor import com.artemchep.keyguard.common.service.crypto.CryptoGenerator import com.artemchep.keyguard.common.service.logging.LogRepository import com.artemchep.keyguard.common.service.text.Base64Service +import com.artemchep.keyguard.common.usecase.GetPasswordStrength import com.artemchep.keyguard.core.store.DatabaseManager import com.artemchep.keyguard.core.store.DatabaseSyncer import com.artemchep.keyguard.core.store.bitwarden.BitwardenCipher @@ -71,6 +72,7 @@ class SyncEngine( private val cryptoGenerator: CryptoGenerator, private val cipherEncryptor: CipherEncryptor, private val logRepository: LogRepository, + private val getPasswordStrength: GetPasswordStrength, private val user: BitwardenToken, private val syncer: DatabaseSyncer, ) { @@ -500,7 +502,7 @@ class SyncEngine( ) .let { remoteDecoded -> // inject the local model into newly decoded remote one - local?.let { merge(remoteDecoded, it) } ?: remoteDecoded + merge(remoteDecoded, local, getPasswordStrength) } }, remoteDeleteById = { id -> @@ -554,7 +556,7 @@ class SyncEngine( // types type = BitwardenCipher.Type.Card, ) - localOrNull?.let { merge(model, it) } ?: model + merge(model, localOrNull, getPasswordStrength) }, remotePut = { (r, local) -> val ciphersApi = user.env.back().api.ciphers @@ -661,7 +663,7 @@ class SyncEngine( ) .let { remoteDecoded -> // inject the local model into newly decoded remote one - merge(remoteDecoded, r.source) + merge(remoteDecoded, r.source, getPasswordStrength) } }, onLog = { msg, logLevel -> diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/provider/bitwarden/api/fff.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/provider/bitwarden/api/fff.kt index e29cb1c7..0597e909 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/provider/bitwarden/api/fff.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/provider/bitwarden/api/fff.kt @@ -12,6 +12,7 @@ import com.artemchep.keyguard.common.io.ioEffect import com.artemchep.keyguard.common.io.measure import com.artemchep.keyguard.common.io.parallel import com.artemchep.keyguard.common.service.logging.LogLevel +import com.artemchep.keyguard.common.usecase.GetPasswordStrength import com.artemchep.keyguard.core.store.bitwarden.BitwardenCipher import com.artemchep.keyguard.core.store.bitwarden.BitwardenService import com.artemchep.keyguard.provider.bitwarden.sync.SyncManager @@ -24,12 +25,13 @@ import kotlin.Throwable import kotlin.Unit import kotlin.let -fun merge( +suspend fun merge( remote: BitwardenCipher, - local: BitwardenCipher, + local: BitwardenCipher?, + getPasswordStrength: GetPasswordStrength, ): BitwardenCipher { val attachments = remote.attachments.toMutableList() - local.attachments.forEachIndexed { localIndex, attachment -> + local?.attachments?.forEachIndexed { localIndex, attachment -> val localAttachment = attachment as? BitwardenCipher.Attachment.Local ?: return@forEachIndexed @@ -57,8 +59,34 @@ fun merge( } } - val ignoredAlerts = local.ignoredAlerts + var login = remote.login + // Calculate or copy over the password strength of + // the password. + if (remote.login != null) run { + val password = remote.login.password + ?: return@run + val strength = local?.login?.passwordStrength + .takeIf { local?.login?.password == remote.login.password } + // Generate a password strength badge. + ?: getPasswordStrength(password) + .attempt() + .bind() + .getOrNull() + ?.let { ps -> + BitwardenCipher.Login.PasswordStrength( + password = password, + crackTimeSeconds = ps.crackTimeSeconds, + version = ps.version, + ) + } + login = login?.copy( + passwordStrength = strength, + ) + } + + val ignoredAlerts = local?.ignoredAlerts.orEmpty() return remote.copy( + login = login, attachments = attachments, ignoredAlerts = ignoredAlerts, ) @@ -86,9 +114,9 @@ suspend fun < shouldOverwrite: (Local, Remote) -> Boolean = { _, _ -> false }, remoteItems: Collection, remoteLens: SyncManager.Lens, - remoteDecoder: (Remote, Local?) -> RemoteDecoded, + remoteDecoder: suspend (Remote, Local?) -> RemoteDecoded, remoteDecodedToString: (RemoteDecoded) -> String = { it.toString() }, - remoteDecodedFallback: (Remote, Local?, Throwable) -> RemoteDecoded, + remoteDecodedFallback: suspend (Remote, Local?, Throwable) -> RemoteDecoded, remoteDeleteById: suspend (String) -> Unit, remotePut: suspend RemotePutScope.(LocalDecoded) -> RemoteDecoded, onLog: (String, LogLevel) -> Unit, diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/provider/bitwarden/usecase/internal/SyncByTokenImpl.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/provider/bitwarden/usecase/internal/SyncByTokenImpl.kt index a5a5dece..ffb06508 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/provider/bitwarden/usecase/internal/SyncByTokenImpl.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/provider/bitwarden/usecase/internal/SyncByTokenImpl.kt @@ -13,6 +13,7 @@ import com.artemchep.keyguard.common.service.crypto.CipherEncryptor import com.artemchep.keyguard.common.service.crypto.CryptoGenerator import com.artemchep.keyguard.common.service.logging.LogRepository import com.artemchep.keyguard.common.service.text.Base64Service +import com.artemchep.keyguard.common.usecase.GetPasswordStrength import com.artemchep.keyguard.common.usecase.Watchdog import com.artemchep.keyguard.core.store.DatabaseManager import com.artemchep.keyguard.core.store.DatabaseSyncer @@ -38,6 +39,7 @@ class SyncByTokenImpl( private val cipherEncryptor: CipherEncryptor, private val cryptoGenerator: CryptoGenerator, private val base64Service: Base64Service, + private val getPasswordStrength: GetPasswordStrength, private val json: Json, private val httpClient: HttpClient, private val db: DatabaseManager, @@ -55,6 +57,7 @@ class SyncByTokenImpl( cipherEncryptor = directDI.instance(), cryptoGenerator = directDI.instance(), base64Service = directDI.instance(), + getPasswordStrength = directDI.instance(), json = directDI.instance(), httpClient = directDI.instance(), db = directDI.instance(), @@ -83,6 +86,7 @@ class SyncByTokenImpl( cryptoGenerator = cryptoGenerator, cipherEncryptor = cipherEncryptor, logRepository = logRepository, + getPasswordStrength = getPasswordStrength, user = latestUser, syncer = dbSyncer, )