KeyStoreAuthenticationService.kt

package de.pflugradts.passbird.application.security
import de.pflugradts.kotlinextensions.TryResult
import de.pflugradts.kotlinextensions.TryResult.Companion.failure
import de.pflugradts.passbird.application.KeyStoreAdapterPort
import de.pflugradts.passbird.application.SecureInputUnavailableException
import de.pflugradts.passbird.application.UserInterfaceAdapterPort
import de.pflugradts.passbird.application.configuration.ReadableConfiguration
import de.pflugradts.passbird.application.toDirectory
import de.pflugradts.passbird.application.toFileName
import de.pflugradts.passbird.application.util.SystemOperation
import de.pflugradts.passbird.domain.model.shell.Shell
import de.pflugradts.passbird.domain.model.shell.Shell.Companion.shellOf
import de.pflugradts.passbird.domain.model.transfer.Output.Companion.outputOf
class KeyStoreAuthenticationService constructor(
    private val configuration: ReadableConfiguration,
    private val keyStoreAdapterPort: KeyStoreAdapterPort,
    private val userInterfaceAdapterPort: UserInterfaceAdapterPort,
    private val systemOperation: SystemOperation,
) {
    fun authenticate(maxAttempts: Int = 3, prompt: String = "Enter key: "): TryResult<Shell> {
        var result = authenticate(prompt)
        repeat(maxAttempts - 1) {
            if (result.failure) {
                result = authenticate(prompt)
            }
        }
        return result
    }
    fun keyStorePath() = systemOperation.resolvePath(
        configuration.adapter.keyStore.location.toDirectory(),
        ReadableConfiguration.KEYSTORE_FILENAME.toFileName(),
    )
    private fun authenticate(prompt: String): TryResult<Shell> = try {
        keyStoreAdapterPort.loadKey(
            userInterfaceAdapterPort.receiveSecurely(outputOf(shellOf(prompt))).toPlainShell(),
            keyStorePath(),
        )
    } catch (ex: SecureInputUnavailableException) {
        failure(ex)
    }
}