diff --git a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt new file mode 100644 index 00000000..59e447d2 --- /dev/null +++ b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt @@ -0,0 +1,45 @@ +package com.vauthenticator.server.job + +import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.jdbc.core.queryForList +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.transaction.annotation.Transactional +import java.time.Clock + + +@Transactional +class DatabaseTtlEntryCleanJob( + private val jdbcTemplate: JdbcTemplate, + private val clock: Clock +) { + + @Scheduled(cron = "\${scheduled.database-cleanup.cron}") + fun execute() { + val now = clock.instant().epochSecond + + deleteOldTicket(now) + deleteOldKeys(now) + } + + private fun deleteOldKeys(now: Long) { + val keysToBeDeleted = + jdbcTemplate.queryForList("SELECT key_id,key_purpose FROM KEYS WHERE key_expiration_date_timestamp < ?", now) + keysToBeDeleted.forEach { + jdbcTemplate.update( + "DELETE FROM KEYS WHERE key_id = ? AND key_purpose = ?;", it["key_id"], it["key_purpose"] + ) + } + } + + private fun deleteOldTicket(now: Long) { + val ticketToBeDeleted = + jdbcTemplate.queryForList( + "SELECT ticket FROM TICKET WHERE ttl < ?", + arrayOf(now) + ) + ticketToBeDeleted.forEach { + jdbcTemplate.update("DELETE FROM TICKET WHERE ticket = ?", it) + } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt b/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt new file mode 100644 index 00000000..77d1f82c --- /dev/null +++ b/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt @@ -0,0 +1,58 @@ +package com.vauthenticator.server.job + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.vauthenticator.server.keys.adapter.jdbc.JdbcKeyStorage +import com.vauthenticator.server.keys.domain.* +import com.vauthenticator.server.keys.domain.KeyPurpose.SIGNATURE +import com.vauthenticator.server.support.AccountTestFixture +import com.vauthenticator.server.support.ClientAppFixture +import com.vauthenticator.server.support.JdbcUtils.jdbcTemplate +import com.vauthenticator.server.support.JdbcUtils.resetDb +import com.vauthenticator.server.support.TicketFixture +import com.vauthenticator.server.ticket.adapter.jdbc.JdbcTicketRepository +import com.vauthenticator.server.ticket.domain.TicketId +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import java.time.Clock +import java.time.Duration +import java.util.* + + +class DatabaseTtlEntryCleanJobTest { + + @BeforeEach + fun setUp() { + resetDb() + } + + @Test + fun `when the old entries are deleted`() { + val ticketRepository = JdbcTicketRepository(jdbcTemplate, jacksonObjectMapper()) + val keyStorage = JdbcKeyStorage(jdbcTemplate, Clock.systemDefaultZone()) + + val uut = DatabaseTtlEntryCleanJob(jdbcTemplate, Clock.systemUTC()) + + val kid = Kid("") + val anAccount = AccountTestFixture.anAccount() + val aClientAppId = ClientAppFixture.aClientAppId() + + ticketRepository.store(TicketFixture.ticketFor("A_TICKET", anAccount.email, aClientAppId.content)) + keyStorage.store( + MasterKid(""), + kid, + DataKey(ByteArray(0), Optional.empty()), + KeyType.ASYMMETRIC, + SIGNATURE + ) + keyStorage.keyDeleteJodPlannedFor(kid, Duration.ofSeconds(-200), SIGNATURE) + + uut.execute() + + val actualTicket = ticketRepository.loadFor(TicketId("A_TICKET")) + Assertions.assertTrue(actualTicket.isEmpty) + Assertions.assertThrows(NoSuchElementException::class.java) { + keyStorage.findOne(kid, SIGNATURE) + } + } +} \ No newline at end of file