CommonPasswordServiceCapabilities.kt
package de.pflugradts.passbird.domain.service.password
import de.pflugradts.kotlinextensions.Option
import de.pflugradts.kotlinextensions.TryResult
import de.pflugradts.kotlinextensions.toOption
import de.pflugradts.passbird.domain.model.egg.Egg
import de.pflugradts.passbird.domain.model.egg.EggIdMemory
import de.pflugradts.passbird.domain.model.egg.requireValidEggId
import de.pflugradts.passbird.domain.model.event.EggNotFound
import de.pflugradts.passbird.domain.model.shell.EncryptedShell
import de.pflugradts.passbird.domain.model.shell.Shell
import de.pflugradts.passbird.domain.model.slot.Slot
import de.pflugradts.passbird.domain.service.eventhandling.EventRegistry
import de.pflugradts.passbird.domain.service.password.PasswordService.EggNotExistsAction
import de.pflugradts.passbird.domain.service.password.encryption.CryptoProvider
import de.pflugradts.passbird.domain.service.password.tree.EggRepository
import java.util.stream.Stream
abstract class CommonPasswordServiceCapabilities(
private val cryptoProvider: CryptoProvider,
protected val eggRepository: EggRepository,
protected val eventRegistry: EventRegistry,
) {
fun find(eggIdShell: Shell, slot: Slot): Option<Egg> = eggRepository.findAll(slot).findDecrypted(eggIdShell)
fun find(eggIdShell: Shell): Option<Egg> = eggRepository.findAll().findDecrypted(eggIdShell).apply { ifPresent { updateMemory(it) } }
fun findWithoutUpdatingMemory(eggIdShell: Shell): Option<Egg> = eggRepository.findAll().findDecrypted(eggIdShell)
private fun Stream<Egg>.findDecrypted(eggIdShell: Shell) = filter {
decryptedMatches(it.viewEggId(), eggIdShell)
}.findAny().toOption()
private fun EggIdMemory.findDuplicate(egg: Egg): EncryptedShell? {
val eggId = egg.viewEggId()
return find { entry ->
entry.map { decryptedMatches(it, eggId) }.orElse(false)
}?.get()
}
private fun decryptedMatches(encryptedShell: EncryptedShell, shell: Shell): Boolean {
val decryptedShell = decrypted(encryptedShell)
return try {
decryptedShell == shell
} finally {
decryptedShell.scramble()
}
}
private fun decryptedMatches(firstEncryptedShell: EncryptedShell, secondEncryptedShell: EncryptedShell): Boolean {
val firstShell = decrypted(firstEncryptedShell)
val secondShell = decrypted(secondEncryptedShell)
return try {
firstShell == secondShell
} finally {
firstShell.scramble()
secondShell.scramble()
}
}
fun updateMemory(egg: Egg, sync: Boolean = true) = eggRepository.updateMemory(egg, eggRepository.memory().findDuplicate(egg), sync)
fun encrypted(shell: Shell) = cryptoProvider.encrypt(shell)
fun decrypted(encryptedShell: EncryptedShell) = cryptoProvider.decrypt(encryptedShell)
fun processEventsAndSync(): TryResult<Unit> = eggRepository.sync()
.onSuccess { eventRegistry.processEvents() }
.onFailure { eventRegistry.clearEvents() }
fun registerEggNotFound(eggIdShell: Shell) {
eventRegistry.register(EggNotFound(eggIdShell))
eventRegistry.processEvents()
}
fun challengeEggId(shell: Shell) = requireValidEggId(shell)
fun eggExists(eggIdShell: Shell, slot: Slot) = find(eggIdShell, slot).isPresent
fun eggExists(eggIdShell: Shell, eggNotExistsAction: EggNotExistsAction) = find(eggIdShell).let {
if (it.isEmpty && eggNotExistsAction == EggNotExistsAction.CREATE_ENTRY_NOT_EXISTS_EVENT) {
eventRegistry.register(EggNotFound(eggIdShell))
eventRegistry.processEvents()
}
it.isPresent
}
}