Calculate password strength during the initial sync of the ciphers

This commit is contained in:
Artem Chepurnoy 2024-01-23 21:01:13 +02:00
parent fcdb373998
commit 3805164489
No known key found for this signature in database
GPG Key ID: FAC37D0CF674043E
5 changed files with 44 additions and 60 deletions

View File

@ -80,7 +80,7 @@ inline fun <T> IO<T>.handleErrorWith(
inline fun <T> IO<T>.handleError(
crossinline predicate: (Throwable) -> Boolean = { true },
crossinline block: (Throwable) -> T,
crossinline block: suspend (Throwable) -> T,
): IO<T> = ioEffect {
try {
invoke()

View File

@ -143,61 +143,11 @@ private const val ROUTE_NAME = "home"
private val vaultRoute = VaultRoute()
@Composable
fun HomeEffect() {
val db by rememberInstance<DatabaseManager>()
val ps by rememberInstance<GetPasswordStrength>()
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(

View File

@ -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 ->

View File

@ -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<Remote>,
remoteLens: SyncManager.Lens<Remote>,
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<Remote>.(LocalDecoded) -> RemoteDecoded,
onLog: (String, LogLevel) -> Unit,

View File

@ -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,
)