PasswordTreeReader.kt
package de.pflugradts.passbird.adapter.passwordtree
import de.pflugradts.kotlinextensions.tryCatching
import de.pflugradts.passbird.application.configuration.ReadableConfiguration
import de.pflugradts.passbird.application.configuration.ReadableConfiguration.Companion.PASSWORD_TREE_FILENAME
import de.pflugradts.passbird.application.failure.DecryptPasswordTreeFailure
import de.pflugradts.passbird.application.failure.reportFailure
import de.pflugradts.passbird.application.passwordtree.PasswordTreeEnvelope
import de.pflugradts.passbird.application.passwordtree.PasswordTreePayloadReader
import de.pflugradts.passbird.application.toDirectory
import de.pflugradts.passbird.application.toFileName
import de.pflugradts.passbird.application.util.FAILURE_EXIT_STATUS
import de.pflugradts.passbird.application.util.SystemOperation
import de.pflugradts.passbird.domain.model.shell.EncryptedShell.Companion.encryptedShellOf
import de.pflugradts.passbird.domain.model.shell.Shell.Companion.emptyShell
import de.pflugradts.passbird.domain.service.password.encryption.CryptoProvider
import de.pflugradts.passbird.domain.service.password.tree.EggStreamSupplier
class PasswordTreeReader constructor(
private val systemOperation: SystemOperation,
private val configuration: ReadableConfiguration,
private val cryptoProvider: CryptoProvider,
private val passwordTreeEnvelope: PasswordTreeEnvelope,
private val passwordTreePayloadReader: PasswordTreePayloadReader,
) {
fun restore(): EggStreamSupplier {
val shell = readFromDisk()
val snapshot = tryCatching {
try {
passwordTreePayloadReader.read(shell)
} finally {
shell.scramble()
}
}
.onFailure(::abortRestore)
.getOrNull()!!
return EggStreamSupplier({ snapshot.eggs.stream() }, snapshot.memory, snapshot.favorites, snapshot.nests)
}
private fun readFromDisk() = tryCatching {
if (!systemOperation.exists(filePath)) {
return@tryCatching emptyShell()
}
systemOperation.readBytesFromFile(filePath).let {
if (it.isEmpty()) {
throw IllegalStateException("Unsupported password tree format.")
}
cryptoProvider.decrypt(encryptedShellOf(passwordTreeEnvelope.unwrap(it)))
}
}
.onFailure(::abortRestore)
.getOrNull()!!
private fun abortRestore(ex: Exception): Nothing {
reportFailure(DecryptPasswordTreeFailure(filePath, ex))
systemOperation.exit(FAILURE_EXIT_STATUS)
throw ex
}
private val filePath get() =
systemOperation.resolvePath(configuration.adapter.passwordTree.location.toDirectory(), PASSWORD_TREE_FILENAME.toFileName())
}